blending modes are loaded modularly now
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
float blendAdd(float base, float blend) {
|
||||
return min(base+blend,1.0);
|
||||
}
|
||||
|
||||
vec3 blendAdd(vec3 base, vec3 blend) {
|
||||
return min(base+blend,vec3(1.0));
|
||||
}
|
||||
|
||||
vec3 blendAdd(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendAdd(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendAdd)
|
||||
@@ -0,0 +1,9 @@
|
||||
vec3 blendAverage(vec3 base, vec3 blend) {
|
||||
return (base+blend)/2.0;
|
||||
}
|
||||
|
||||
vec3 blendAverage(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendAverage(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendAverage)
|
||||
@@ -0,0 +1,13 @@
|
||||
float blendColorBurn(float base, float blend) {
|
||||
return (blend==0.0)?blend:max((1.0-((1.0-base)/blend)),0.0);
|
||||
}
|
||||
|
||||
vec3 blendColorBurn(vec3 base, vec3 blend) {
|
||||
return vec3(blendColorBurn(base.r,blend.r),blendColorBurn(base.g,blend.g),blendColorBurn(base.b,blend.b));
|
||||
}
|
||||
|
||||
vec3 blendColorBurn(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendColorBurn(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendColorBurn)
|
||||
@@ -0,0 +1,13 @@
|
||||
float blendColorDodge(float base, float blend) {
|
||||
return (blend==1.0)?blend:min(base/(1.0-blend),1.0);
|
||||
}
|
||||
|
||||
vec3 blendColorDodge(vec3 base, vec3 blend) {
|
||||
return vec3(blendColorDodge(base.r,blend.r),blendColorDodge(base.g,blend.g),blendColorDodge(base.b,blend.b));
|
||||
}
|
||||
|
||||
vec3 blendColorDodge(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendColorDodge(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendColorDodge)
|
||||
@@ -0,0 +1,13 @@
|
||||
float blendDarken(float base, float blend) {
|
||||
return min(blend,base);
|
||||
}
|
||||
|
||||
vec3 blendDarken(vec3 base, vec3 blend) {
|
||||
return vec3(blendDarken(base.r,blend.r),blendDarken(base.g,blend.g),blendDarken(base.b,blend.b));
|
||||
}
|
||||
|
||||
vec3 blendDarken(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendDarken(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendDarken)
|
||||
@@ -0,0 +1,9 @@
|
||||
vec3 blendDifference(vec3 base, vec3 blend) {
|
||||
return abs(base-blend);
|
||||
}
|
||||
|
||||
vec3 blendDifference(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendDifference(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendDifference)
|
||||
@@ -0,0 +1,9 @@
|
||||
vec3 blendExclusion(vec3 base, vec3 blend) {
|
||||
return base+blend-2.0*base*blend;
|
||||
}
|
||||
|
||||
vec3 blendExclusion(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendExclusion(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendExclusion)
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma glslify: blendReflect = require(./reflect)
|
||||
|
||||
vec3 blendGlow(vec3 base, vec3 blend) {
|
||||
return blendReflect(blend,base);
|
||||
}
|
||||
|
||||
vec3 blendGlow(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendGlow(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendGlow)
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma glslify: blendOverlay = require(./overlay)
|
||||
|
||||
vec3 blendHardLight(vec3 base, vec3 blend) {
|
||||
return blendOverlay(blend,base);
|
||||
}
|
||||
|
||||
vec3 blendHardLight(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendHardLight(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendHardLight)
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma glslify: blendVividLight = require(./vivid-light)
|
||||
|
||||
float blendHardMix(float base, float blend) {
|
||||
return (blendVividLight(base,blend)<0.5)?0.0:1.0;
|
||||
}
|
||||
|
||||
vec3 blendHardMix(vec3 base, vec3 blend) {
|
||||
return vec3(blendHardMix(base.r,blend.r),blendHardMix(base.g,blend.g),blendHardMix(base.b,blend.b));
|
||||
}
|
||||
|
||||
vec3 blendHardMix(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendHardMix(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendHardMix)
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "io/math.h"
|
||||
#include "ui/labelslider.h"
|
||||
#include "ui/comboboxex.h"
|
||||
#include "project/effectloaders.h"
|
||||
#include "panels/project.h"
|
||||
#include "debug.h"
|
||||
|
||||
@@ -78,9 +79,11 @@ TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em)
|
||||
blend_mode_box->add_combo_item(tr("Normal"), -1);
|
||||
|
||||
// add loaded blending modes
|
||||
olive::effects_loaded.lock();
|
||||
for (int i=0;i<olive::blend_modes.size();i++) {
|
||||
blend_mode_box->add_combo_item(olive::blend_modes.at(i).name, i);
|
||||
}
|
||||
olive::effects_loaded.unlock();
|
||||
|
||||
// set up gizmos
|
||||
top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
float blendLighten(float base, float blend) {
|
||||
return max(blend,base);
|
||||
}
|
||||
|
||||
vec3 blendLighten(vec3 base, vec3 blend) {
|
||||
return vec3(blendLighten(base.r,blend.r),blendLighten(base.g,blend.g),blendLighten(base.b,blend.b));
|
||||
}
|
||||
|
||||
vec3 blendLighten(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendLighten(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendLighten)
|
||||
@@ -0,0 +1,15 @@
|
||||
float blendLinearBurn(float base, float blend) {
|
||||
// Note : Same implementation as BlendSubtractf
|
||||
return max(base+blend-1.0,0.0);
|
||||
}
|
||||
|
||||
vec3 blendLinearBurn(vec3 base, vec3 blend) {
|
||||
// Note : Same implementation as BlendSubtract
|
||||
return max(base+blend-vec3(1.0),vec3(0.0));
|
||||
}
|
||||
|
||||
vec3 blendLinearBurn(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendLinearBurn(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendLinearBurn)
|
||||
@@ -0,0 +1,15 @@
|
||||
float blendLinearDodge(float base, float blend) {
|
||||
// Note : Same implementation as BlendAddf
|
||||
return min(base+blend,1.0);
|
||||
}
|
||||
|
||||
vec3 blendLinearDodge(vec3 base, vec3 blend) {
|
||||
// Note : Same implementation as BlendAdd
|
||||
return min(base+blend,vec3(1.0));
|
||||
}
|
||||
|
||||
vec3 blendLinearDodge(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendLinearDodge(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendLinearDodge)
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma glslify: blendLinearDodge = require(./linear-dodge)
|
||||
#pragma glslify: blendLinearBurn = require(./linear-burn)
|
||||
|
||||
float blendLinearLight(float base, float blend) {
|
||||
return blend<0.5?blendLinearBurn(base,(2.0*blend)):blendLinearDodge(base,(2.0*(blend-0.5)));
|
||||
}
|
||||
|
||||
vec3 blendLinearLight(vec3 base, vec3 blend) {
|
||||
return vec3(blendLinearLight(base.r,blend.r),blendLinearLight(base.g,blend.g),blendLinearLight(base.b,blend.b));
|
||||
}
|
||||
|
||||
vec3 blendLinearLight(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendLinearLight(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendLinearLight)
|
||||
@@ -0,0 +1,9 @@
|
||||
vec3 blendNegation(vec3 base, vec3 blend) {
|
||||
return vec3(1.0)-abs(vec3(1.0)-base-blend);
|
||||
}
|
||||
|
||||
vec3 blendNegation(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendNegation(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendNegation)
|
||||
@@ -0,0 +1,9 @@
|
||||
vec3 blendNormal(vec3 base, vec3 blend) {
|
||||
return blend;
|
||||
}
|
||||
|
||||
vec3 blendNormal(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendNormal(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendNormal)
|
||||
@@ -0,0 +1,13 @@
|
||||
float blendOverlay(float base, float blend) {
|
||||
return base<0.5?(2.0*base*blend):(1.0-2.0*(1.0-base)*(1.0-blend));
|
||||
}
|
||||
|
||||
vec3 blendOverlay(vec3 base, vec3 blend) {
|
||||
return vec3(blendOverlay(base.r,blend.r),blendOverlay(base.g,blend.g),blendOverlay(base.b,blend.b));
|
||||
}
|
||||
|
||||
vec3 blendOverlay(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendOverlay(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendOverlay)
|
||||
@@ -0,0 +1,9 @@
|
||||
vec3 blendPhoenix(vec3 base, vec3 blend) {
|
||||
return min(base,blend)-max(base,blend)+vec3(1.0);
|
||||
}
|
||||
|
||||
vec3 blendPhoenix(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendPhoenix(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendPhoenix)
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma glslify: blendLighten = require(./lighten)
|
||||
#pragma glslify: blendDarken = require(./darken)
|
||||
|
||||
float blendPinLight(float base, float blend) {
|
||||
return (blend<0.5)?blendDarken(base,(2.0*blend)):blendLighten(base,(2.0*(blend-0.5)));
|
||||
}
|
||||
|
||||
vec3 blendPinLight(vec3 base, vec3 blend) {
|
||||
return vec3(blendPinLight(base.r,blend.r),blendPinLight(base.g,blend.g),blendPinLight(base.b,blend.b));
|
||||
}
|
||||
|
||||
vec3 blendPinLight(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendPinLight(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendPinLight)
|
||||
@@ -0,0 +1,13 @@
|
||||
float blendReflect(float base, float blend) {
|
||||
return (blend==1.0)?blend:min(base*base/(1.0-blend),1.0);
|
||||
}
|
||||
|
||||
vec3 blendReflect(vec3 base, vec3 blend) {
|
||||
return vec3(blendReflect(base.r,blend.r),blendReflect(base.g,blend.g),blendReflect(base.b,blend.b));
|
||||
}
|
||||
|
||||
vec3 blendReflect(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendReflect(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendReflect)
|
||||
@@ -0,0 +1,13 @@
|
||||
float blendScreen(float base, float blend) {
|
||||
return 1.0-((1.0-base)*(1.0-blend));
|
||||
}
|
||||
|
||||
vec3 blendScreen(vec3 base, vec3 blend) {
|
||||
return vec3(blendScreen(base.r,blend.r),blendScreen(base.g,blend.g),blendScreen(base.b,blend.b));
|
||||
}
|
||||
|
||||
vec3 blendScreen(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendScreen(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendScreen)
|
||||
@@ -0,0 +1,13 @@
|
||||
float blendSoftLight(float base, float blend) {
|
||||
return (blend<0.5)?(2.0*base*blend+base*base*(1.0-2.0*blend)):(sqrt(base)*(2.0*blend-1.0)+2.0*base*(1.0-blend));
|
||||
}
|
||||
|
||||
vec3 blendSoftLight(vec3 base, vec3 blend) {
|
||||
return vec3(blendSoftLight(base.r,blend.r),blendSoftLight(base.g,blend.g),blendSoftLight(base.b,blend.b));
|
||||
}
|
||||
|
||||
vec3 blendSoftLight(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendSoftLight(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendSoftLight)
|
||||
@@ -0,0 +1,13 @@
|
||||
float blendSubstract(float base, float blend) {
|
||||
return max(base+blend-1.0,0.0);
|
||||
}
|
||||
|
||||
vec3 blendSubstract(vec3 base, vec3 blend) {
|
||||
return max(base+blend-vec3(1.0),vec3(0.0));
|
||||
}
|
||||
|
||||
vec3 blendSubstract(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendSubstract(base, blend) * opacity + blend * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendSubstract)
|
||||
@@ -0,0 +1,13 @@
|
||||
float blendSubtract(float base, float blend) {
|
||||
return max(base+blend-1.0,0.0);
|
||||
}
|
||||
|
||||
vec3 blendSubtract(vec3 base, vec3 blend) {
|
||||
return max(base+blend-vec3(1.0),vec3(0.0));
|
||||
}
|
||||
|
||||
vec3 blendSubtract(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendSubtract(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendSubtract)
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma glslify: blendColorDodge = require(./color-dodge)
|
||||
#pragma glslify: blendColorBurn = require(./color-burn)
|
||||
|
||||
float blendVividLight(float base, float blend) {
|
||||
return (blend<0.5)?blendColorBurn(base,(2.0*blend)):blendColorDodge(base,(2.0*(blend-0.5)));
|
||||
}
|
||||
|
||||
vec3 blendVividLight(vec3 base, vec3 blend) {
|
||||
return vec3(blendVividLight(base.r,blend.r),blendVividLight(base.g,blend.g),blendVividLight(base.b,blend.b));
|
||||
}
|
||||
|
||||
vec3 blendVividLight(vec3 base, vec3 blend, float opacity) {
|
||||
return (blendVividLight(base, blend) * opacity + base * (1.0 - opacity));
|
||||
}
|
||||
|
||||
#pragma glslify: export(blendVividLight)
|
||||
+3
-2
@@ -32,6 +32,7 @@
|
||||
#include "rendering/renderfunctions.h"
|
||||
#include "io/previewgenerator.h"
|
||||
#include "effects/internal/voideffect.h"
|
||||
#include "project/effectloaders.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <QFile>
|
||||
@@ -115,7 +116,7 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) {
|
||||
|
||||
// Effect loading occurs in another thread, and while it's usually very quick, just for safety we wait here
|
||||
// for all the effects to finish loading
|
||||
panel_effect_controls->effects_loaded.lock();
|
||||
olive::effects_loaded.lock();
|
||||
|
||||
const EffectMeta* meta = nullptr;
|
||||
|
||||
@@ -124,7 +125,7 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) {
|
||||
meta = get_meta_from_name(effect_name);
|
||||
}
|
||||
|
||||
panel_effect_controls->effects_loaded.unlock();
|
||||
olive::effects_loaded.unlock();
|
||||
|
||||
int type;
|
||||
if (tag == "opening") {
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "ui/collapsiblewidget.h"
|
||||
#include "project/sequence.h"
|
||||
#include "project/undo.h"
|
||||
#include "project/effectloaders.h"
|
||||
#include "panels/project.h"
|
||||
#include "panels/timeline.h"
|
||||
#include "panels/viewer.h"
|
||||
@@ -194,7 +195,7 @@ void EffectControls::show_effect_menu(int type, int subtype) {
|
||||
effect_menu_type = type;
|
||||
effect_menu_subtype = subtype;
|
||||
|
||||
effects_loaded.lock();
|
||||
olive::effects_loaded.lock();
|
||||
|
||||
QMenu effects_menu(this);
|
||||
effects_menu.setToolTipsVisible(true);
|
||||
@@ -254,7 +255,7 @@ void EffectControls::show_effect_menu(int type, int subtype) {
|
||||
}
|
||||
}
|
||||
|
||||
effects_loaded.unlock();
|
||||
olive::effects_loaded.unlock();
|
||||
|
||||
connect(&effects_menu, SIGNAL(triggered(QAction*)), this, SLOT(menu_select(QAction*)));
|
||||
effects_menu.exec(QCursor::pos());
|
||||
|
||||
@@ -74,8 +74,6 @@ public:
|
||||
ResizableScrollBar* horizontalScrollBar;
|
||||
QScrollBar* verticalScrollBar;
|
||||
|
||||
QMutex effects_loaded;
|
||||
|
||||
void add_effect_paste_action(QMenu* menu);
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
QMutex olive::effects_loaded;
|
||||
|
||||
#ifndef NOFREI0R
|
||||
#include <frei0r.h>
|
||||
typedef void (*f0rGetPluginInfo)(f0r_plugin_info_t* info);
|
||||
@@ -287,15 +289,27 @@ void GenerateBlendingShader()
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine();
|
||||
if (line.startsWith("#olive name ")) {
|
||||
if (line.length() > 0 && line.at(0) == '#') {
|
||||
|
||||
// The blending mode can specify its own name
|
||||
olive::blend_modes[i].name = line.mid(12);
|
||||
if (line.startsWith("#olive name ")) {
|
||||
|
||||
} else if (line.startsWith("#pragma glslify: export(")) {
|
||||
// The blending mode can specify its own name
|
||||
olive::blend_modes[i].name = line.mid(12);
|
||||
|
||||
// Get function name
|
||||
olive::blend_modes[i].function_name = line.mid(24, line.length()-25);
|
||||
} else if (line.startsWith("#pragma glslify: export(")) {
|
||||
|
||||
// Get function name
|
||||
olive::blend_modes[i].function_name = line.mid(24, line.length()-25);
|
||||
|
||||
} else if (line.contains("require")) {
|
||||
|
||||
// This function wanted to include an external shader
|
||||
|
||||
int index_of_last_bracked = line.lastIndexOf('(') + 1;
|
||||
|
||||
qDebug() << "this blend mode wanted:" << line.mid(index_of_last_bracked, line.length() - index_of_last_bracked - 1);
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
@@ -324,7 +338,7 @@ void GenerateBlendingShader()
|
||||
olive::generated_blending_shader.append(QString(" else if (blendmode == %1) {\n").arg(i));
|
||||
}
|
||||
|
||||
olive::generated_blending_shader.append(QString(" return %1(base, blend)\n").arg(olive::blend_modes.at(i).function_name));
|
||||
olive::generated_blending_shader.append(QString(" return %1(base, blend);\n").arg(olive::blend_modes.at(i).function_name));
|
||||
olive::generated_blending_shader.append(" }");
|
||||
}
|
||||
|
||||
@@ -344,7 +358,7 @@ void GenerateBlendingShader()
|
||||
}
|
||||
|
||||
EffectInit::EffectInit() {
|
||||
panel_effect_controls->effects_loaded.lock();
|
||||
olive::effects_loaded.lock();
|
||||
}
|
||||
|
||||
void EffectInit::run() {
|
||||
@@ -355,6 +369,6 @@ void EffectInit::run() {
|
||||
load_frei0r_effects();
|
||||
#endif
|
||||
GenerateBlendingShader();
|
||||
panel_effect_controls->effects_loaded.unlock();
|
||||
olive::effects_loaded.unlock();
|
||||
qInfo() << "Finished initializing effects";
|
||||
}
|
||||
|
||||
@@ -23,6 +23,11 @@
|
||||
|
||||
#include <QList>
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
|
||||
namespace olive {
|
||||
extern QMutex effects_loaded;
|
||||
}
|
||||
|
||||
void init_effects();
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace OCIO = OCIO_NAMESPACE;
|
||||
|
||||
#include "rendering/renderfunctions.h"
|
||||
#include "project/sequence.h"
|
||||
#include "project/effectloaders.h"
|
||||
|
||||
RenderThread::RenderThread() :
|
||||
gizmos(nullptr),
|
||||
@@ -99,7 +100,9 @@ void RenderThread::run() {
|
||||
|
||||
blend_mode_program = new QOpenGLShaderProgram();
|
||||
blend_mode_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/internalshaders/common.vert");
|
||||
olive::effects_loaded.lock();
|
||||
blend_mode_program->addShaderFromSourceCode(QOpenGLShader::Fragment, olive::generated_blending_shader);
|
||||
olive::effects_loaded.unlock();
|
||||
blend_mode_program->link();
|
||||
|
||||
premultiply_program = new QOpenGLShaderProgram();
|
||||
|
||||
Reference in New Issue
Block a user