wip
This commit is contained in:
parent
bd8d0d78bf
commit
9fb0f7357a
|
@ -4,16 +4,28 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.root" class="_gaps">
|
<MkFolder :defaultOpen="true">
|
||||||
<div v-for="[k, v] in Object.entries(fx.params)" :key="k">
|
<template #label>{{ fx.id }}</template>
|
||||||
<MkSwitch v-if="v.type === 'boolean'" v-model="layer.params[k]">
|
<template #footer>
|
||||||
<template #label>{{ k }}</template>
|
<MkButton @click="emit('del')">{{ i18n.ts.remove }}</MkButton>
|
||||||
</MkSwitch>
|
</template>
|
||||||
<MkRange v-else-if="v.type === 'number'" v-model="layer.params[k]" continuousUpdate :min="v.min" :max="v.max" :step="v.step">
|
|
||||||
<template #label>{{ k }}</template>
|
<div :class="$style.root" class="_gaps">
|
||||||
</MkRange>
|
<div v-for="[k, v] in Object.entries(fx.params)" :key="k">
|
||||||
|
<MkSwitch v-if="v.type === 'boolean'" v-model="layer.params[k]">
|
||||||
|
<template #label>{{ k }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
<MkRange v-else-if="v.type === 'number'" v-model="layer.params[k]" continuousUpdate :min="v.min" :max="v.max" :step="v.step">
|
||||||
|
<template #label>{{ k }}</template>
|
||||||
|
</MkRange>
|
||||||
|
<div v-else-if="v.type === 'seed'">
|
||||||
|
<MkRange v-model="layer.params[k]" continuousUpdate type="number" :min="0" :max="10000" :step="1">
|
||||||
|
<template #label>{{ k }}</template>
|
||||||
|
</MkRange>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</MkFolder>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -22,7 +34,7 @@ import { v4 as uuid } from 'uuid';
|
||||||
import type { ImageEffectorLayer } from '@/utility/image-effector/ImageEffector.js';
|
import type { ImageEffectorLayer } from '@/utility/image-effector/ImageEffector.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { FXS, ImageEffector } from '@/utility/image-effector/ImageEffector.js';
|
import { FXS, ImageEffector } from '@/utility/image-effector/ImageEffector.js';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
|
@ -40,6 +52,9 @@ if (fx == null) {
|
||||||
throw new Error(`Unrecognized effect: ${layer.value.fxId}`);
|
throw new Error(`Unrecognized effect: ${layer.value.fxId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'del'): void;
|
||||||
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style module>
|
<style module>
|
||||||
|
|
|
@ -31,6 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
v-for="(layer, i) in layers"
|
v-for="(layer, i) in layers"
|
||||||
:key="layer.id"
|
:key="layer.id"
|
||||||
v-model:layer="layers[i]"
|
v-model:layer="layers[i]"
|
||||||
|
@del="onLayerDelete(layer)"
|
||||||
></XLayer>
|
></XLayer>
|
||||||
|
|
||||||
<MkButton rounded primary @click="addEffect"><i class="ti ti-plus"></i></MkButton>
|
<MkButton rounded primary @click="addEffect"><i class="ti ti-plus"></i></MkButton>
|
||||||
|
@ -81,7 +82,7 @@ watch(layers, async () => {
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
|
|
||||||
function addEffect(ev: MouseEvent) {
|
function addEffect(ev: MouseEvent) {
|
||||||
os.popupMenu(FXS.map((fx) => ({
|
os.popupMenu(FXS.filter(fx => fx.id !== 'watermarkPlacement').map((fx) => ({
|
||||||
text: fx.id,
|
text: fx.id,
|
||||||
action: () => {
|
action: () => {
|
||||||
layers.push({
|
layers.push({
|
||||||
|
@ -93,6 +94,13 @@ function addEffect(ev: MouseEvent) {
|
||||||
})), ev.currentTarget ?? ev.target);
|
})), ev.currentTarget ?? ev.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onLayerDelete(layer: ImageEffectorLayer) {
|
||||||
|
const index = layers.indexOf(layer);
|
||||||
|
if (index !== -1) {
|
||||||
|
layers.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const canvasEl = useTemplateRef('canvasEl');
|
const canvasEl = useTemplateRef('canvasEl');
|
||||||
|
|
||||||
let renderer: ImageEffector | null = null;
|
let renderer: ImageEffector | null = null;
|
||||||
|
|
|
@ -5,12 +5,14 @@
|
||||||
|
|
||||||
import { getProxiedImageUrl } from '../media-proxy.js';
|
import { getProxiedImageUrl } from '../media-proxy.js';
|
||||||
import { FX_chromaticAberration } from './fxs/chromaticAberration.js';
|
import { FX_chromaticAberration } from './fxs/chromaticAberration.js';
|
||||||
|
import { FX_glitch } from './fxs/glitch.js';
|
||||||
import { FX_watermarkPlacement } from './fxs/watermarkPlacement.js';
|
import { FX_watermarkPlacement } from './fxs/watermarkPlacement.js';
|
||||||
|
|
||||||
type ParamTypeToPrimitive = {
|
type ParamTypeToPrimitive = {
|
||||||
'number': number;
|
'number': number;
|
||||||
'boolean': boolean;
|
'boolean': boolean;
|
||||||
'align': { x: 'left' | 'center' | 'right'; y: 'top' | 'center' | 'bottom'; };
|
'align': { x: 'left' | 'center' | 'right'; y: 'top' | 'center' | 'bottom'; };
|
||||||
|
'seed': number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ImageEffectorFxParamDefs = Record<string, {
|
type ImageEffectorFxParamDefs = Record<string, {
|
||||||
|
@ -46,6 +48,7 @@ export type ImageEffectorFx<ID extends string, P extends ImageEffectorFxParamDef
|
||||||
export const FXS = [
|
export const FXS = [
|
||||||
FX_watermarkPlacement,
|
FX_watermarkPlacement,
|
||||||
FX_chromaticAberration,
|
FX_chromaticAberration,
|
||||||
|
FX_glitch,
|
||||||
] as const satisfies ImageEffectorFx<string, any>[];
|
] as const satisfies ImageEffectorFx<string, any>[];
|
||||||
|
|
||||||
export type ImageEffectorLayerOf<
|
export type ImageEffectorLayerOf<
|
||||||
|
|
|
@ -28,24 +28,14 @@ void main() {
|
||||||
float normalisedValue = length((in_uv - 0.5) * 2.0);
|
float normalisedValue = length((in_uv - 0.5) * 2.0);
|
||||||
float strength = clamp((normalisedValue - u_start) * (1.0 / (1.0 - u_start)), 0.0, 1.0);
|
float strength = clamp((normalisedValue - u_start) * (1.0 / (1.0 - u_start)), 0.0, 1.0);
|
||||||
|
|
||||||
//vec2 vector = normalize((in_uv - (size / 2.0)) / size);
|
|
||||||
//vec2 vector = in_uv;
|
|
||||||
vec2 vector = (u_normalize ? normalize(in_uv - vec2(0.5)) : in_uv - vec2(0.5));
|
vec2 vector = (u_normalize ? normalize(in_uv - vec2(0.5)) : in_uv - vec2(0.5));
|
||||||
vec2 velocity = vector * strength * u_amount;
|
vec2 velocity = vector * strength * u_amount;
|
||||||
|
|
||||||
//vec2 rOffset = -vector * strength * (u_amount * 1.0);
|
|
||||||
//vec2 gOffset = -vector * strength * (u_amount * 1.5);
|
|
||||||
//vec2 bOffset = -vector * strength * (u_amount * 2.0);
|
|
||||||
|
|
||||||
//vec2 rOffset = -vector * strength * (u_amount * 0.5);
|
|
||||||
//vec2 gOffset = -vector * strength * (u_amount * 1.0);
|
|
||||||
//vec2 bOffset = -vector * strength * (u_amount * 2.0);
|
|
||||||
|
|
||||||
vec2 rOffset = -vector * strength * (u_amount * r_strength);
|
vec2 rOffset = -vector * strength * (u_amount * r_strength);
|
||||||
vec2 gOffset = -vector * strength * (u_amount * g_strength);
|
vec2 gOffset = -vector * strength * (u_amount * g_strength);
|
||||||
vec2 bOffset = -vector * strength * (u_amount * b_strength);
|
vec2 bOffset = -vector * strength * (u_amount * b_strength);
|
||||||
|
|
||||||
for (int i=0; i < samples; i++) {
|
for (int i = 0; i < samples; i++) {
|
||||||
accumulator.r += texture(u_texture, in_uv + rOffset).r;
|
accumulator.r += texture(u_texture, in_uv + rOffset).r;
|
||||||
rOffset -= velocity / float(samples);
|
rOffset -= velocity / float(samples);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import seedrandom from 'seedrandom';
|
||||||
|
import { defineImageEffectorFx } from '../ImageEffector.js';
|
||||||
|
|
||||||
|
const shader = `#version 300 es
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
in vec2 in_uv;
|
||||||
|
uniform sampler2D u_texture;
|
||||||
|
uniform vec2 u_resolution;
|
||||||
|
uniform int u_amount;
|
||||||
|
uniform float u_shiftStrengths[128];
|
||||||
|
uniform float u_shiftOrigins[128];
|
||||||
|
uniform float u_shiftHeights[128];
|
||||||
|
uniform float u_channelShift;
|
||||||
|
out vec4 out_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float v = 0.0;
|
||||||
|
|
||||||
|
for (int i = 0; i < u_amount; i++) {
|
||||||
|
if (in_uv.y > (u_shiftOrigins[i] - u_shiftHeights[i]) && in_uv.y < (u_shiftOrigins[i] + u_shiftHeights[i])) {
|
||||||
|
v += u_shiftStrengths[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float r = texture(u_texture, vec2(in_uv.x + (v * (1.0 + u_channelShift)), in_uv.y)).r;
|
||||||
|
float g = texture(u_texture, vec2(in_uv.x + v, in_uv.y)).g;
|
||||||
|
float b = texture(u_texture, vec2(in_uv.x + (v * (1.0 + (u_channelShift / 2.0))), in_uv.y)).b;
|
||||||
|
float a = texture(u_texture, vec2(in_uv.x + v, in_uv.y)).a;
|
||||||
|
out_color = vec4(r, g, b, a);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FX_glitch = defineImageEffectorFx({
|
||||||
|
id: 'glitch' as const,
|
||||||
|
shader,
|
||||||
|
params: {
|
||||||
|
amount: {
|
||||||
|
type: 'number' as const,
|
||||||
|
default: 3,
|
||||||
|
min: 1,
|
||||||
|
max: 100,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
|
strength: {
|
||||||
|
type: 'number' as const,
|
||||||
|
default: 5,
|
||||||
|
min: -100,
|
||||||
|
max: 100,
|
||||||
|
step: 0.01,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: 'number' as const,
|
||||||
|
default: 20,
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 0.01,
|
||||||
|
},
|
||||||
|
channelShift: {
|
||||||
|
type: 'number' as const,
|
||||||
|
default: 0.5,
|
||||||
|
min: 0,
|
||||||
|
max: 10,
|
||||||
|
step: 0.01,
|
||||||
|
},
|
||||||
|
seed: {
|
||||||
|
type: 'seed' as const,
|
||||||
|
default: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
main: ({ gl, program, params, preTexture }) => {
|
||||||
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
|
gl.bindTexture(gl.TEXTURE_2D, preTexture);
|
||||||
|
const u_texture = gl.getUniformLocation(program, 'u_texture');
|
||||||
|
gl.uniform1i(u_texture, 0);
|
||||||
|
|
||||||
|
const u_amount = gl.getUniformLocation(program, 'u_amount');
|
||||||
|
gl.uniform1i(u_amount, params.amount);
|
||||||
|
|
||||||
|
const u_channelShift = gl.getUniformLocation(program, 'u_channelShift');
|
||||||
|
gl.uniform1f(u_channelShift, params.channelShift);
|
||||||
|
|
||||||
|
const rnd = seedrandom(params.seed.toString());
|
||||||
|
|
||||||
|
for (let i = 0; i < params.amount; i++) {
|
||||||
|
const o = gl.getUniformLocation(program, `u_shiftOrigins[${i.toString()}]`);
|
||||||
|
gl.uniform1f(o, rnd());
|
||||||
|
|
||||||
|
const s = gl.getUniformLocation(program, `u_shiftStrengths[${i.toString()}]`);
|
||||||
|
gl.uniform1f(s, (1 - (rnd() * 2)) * (params.strength / 100));
|
||||||
|
|
||||||
|
const h = gl.getUniformLocation(program, `u_shiftHeights[${i.toString()}]`);
|
||||||
|
gl.uniform1f(h, rnd() * (params.size / 100));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue