feat(all): 后处理迁移
This commit is contained in:
parent
986da8ce42
commit
4cc239d660
@ -226,6 +226,27 @@ export default {
|
||||
"Post processing": "后期处理",
|
||||
postProcessing: {
|
||||
"Anti-aliasing": "抗锯齿",
|
||||
"Quality Preset": "质量预设",
|
||||
"Tone Mapping": "色调映射",
|
||||
"Exposure": "曝光度",
|
||||
"SSAO": "环境光遮蔽",
|
||||
"Samples": "采样数",
|
||||
"Rings": "环数",
|
||||
"Bias": "偏置",
|
||||
"Fade": "衰减",
|
||||
"Luminance Influence": "亮度影响",
|
||||
"Min Radius Scale": "最小半径缩放",
|
||||
"Depth-aware Upsampling": "深度感知上采样",
|
||||
"Distance Threshold": "距离阈值",
|
||||
"Distance Falloff": "距离衰减",
|
||||
"Range Threshold": "范围阈值",
|
||||
"Range Falloff": "范围衰减",
|
||||
"World Distance Threshold": "世界距离阈值",
|
||||
"World Distance Falloff": "世界距离衰减",
|
||||
"World Proximity Threshold": "世界邻近阈值",
|
||||
"World Proximity Falloff": "世界邻近衰减",
|
||||
"Colorize": "颜色化",
|
||||
"AO Color": "遮蔽颜色",
|
||||
"Outline": "描边线",
|
||||
"Edge Strength": "边缘强度",
|
||||
"Edge Glow": "边缘发光",
|
||||
@ -237,6 +258,57 @@ export default {
|
||||
"Radius": "半径",
|
||||
"Threshold": "阈值",
|
||||
"Strength": "强度",
|
||||
"Brightness Contrast": "亮度对比度",
|
||||
"Brightness": "亮度",
|
||||
"Contrast": "对比度",
|
||||
"Chromatic Aberration": "色差",
|
||||
"Offset X": "偏移 X",
|
||||
"Offset Y": "偏移 Y",
|
||||
"Radial Modulation": "径向调制",
|
||||
"Modulation Offset": "调制偏移",
|
||||
"Color Depth": "色深",
|
||||
"Bits": "位数",
|
||||
"Hue Saturation": "色相饱和度",
|
||||
"Hue": "色相",
|
||||
"Saturation": "饱和度",
|
||||
"Tilt Shift": "移轴模糊",
|
||||
"Offset": "偏移",
|
||||
"Rotation": "旋转",
|
||||
"Focus Area": "焦点区域",
|
||||
"Feather": "羽化",
|
||||
"Scanline": "扫描线",
|
||||
"Density": "密度",
|
||||
"Scroll Speed": "滚动速度",
|
||||
"Glitch": "故障",
|
||||
"Glitch Mode": "故障模式",
|
||||
"Sporadic": "偶发",
|
||||
"Constant Mild": "持续轻微",
|
||||
"Constant Wild": "持续剧烈",
|
||||
"Delay Min": "延迟最小值",
|
||||
"Delay Max": "延迟最大值",
|
||||
"Duration Min": "持续最小值",
|
||||
"Duration Max": "持续最大值",
|
||||
"Strength Min": "强度最小值",
|
||||
"Strength Max": "强度最大值",
|
||||
"Ratio": "出现概率",
|
||||
"Lens Distortion": "镜头畸变",
|
||||
"Distortion X": "畸变 X",
|
||||
"Distortion Y": "畸变 Y",
|
||||
"Principal Point X": "主点 X",
|
||||
"Principal Point Y": "主点 Y",
|
||||
"Focal Length X": "焦距 X",
|
||||
"Focal Length Y": "焦距 Y",
|
||||
"Skew": "倾斜",
|
||||
"Shock Wave": "冲击波",
|
||||
"Click on scene to trigger shock wave effect": "点击场景触发冲击波效果",
|
||||
"Amplitude": "振幅",
|
||||
"Wave Size": "波浪大小",
|
||||
"Speed": "传播速度",
|
||||
"Max Radius": "最大半径",
|
||||
"Click Trigger": "点击触发",
|
||||
"Vignette": "暗角",
|
||||
"Darkness": "暗度",
|
||||
"Blend Function": "混合模式",
|
||||
"LUT Color filter":"LUT 颜色滤镜",
|
||||
"Intensity":"强度",
|
||||
"Afterimage":"运动残影",
|
||||
@ -245,6 +317,7 @@ export default {
|
||||
"Focus": "焦距",
|
||||
"Aperture": "孔径",
|
||||
"MaxBlur": "最大模糊",
|
||||
"Resolution Scale": "分辨率缩放",
|
||||
"Pixelate": "像素风",
|
||||
"PixelSize": "像素大小",
|
||||
"NormalEdgeStrength": "法向边缘强度",
|
||||
@ -747,6 +820,7 @@ export default {
|
||||
Open: "开启",
|
||||
Close: "关闭",
|
||||
Enable: "启用",
|
||||
Enabled: "启用",
|
||||
Minimum: "最小值",
|
||||
Maximum: "最大值",
|
||||
Width: "宽度",
|
||||
|
||||
48
packages/editor/src/utils/common/postprocessing.ts
Normal file
48
packages/editor/src/utils/common/postprocessing.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { t } from "@/language";
|
||||
|
||||
// Blend mode options for post-processing blendFunction config.
|
||||
const BLEND_FUNCTION_KEYS = [
|
||||
"NORMAL",
|
||||
"ADD",
|
||||
"ALPHA",
|
||||
"AVERAGE",
|
||||
"COLOR",
|
||||
"COLOR_BURN",
|
||||
"COLOR_DODGE",
|
||||
"DARKEN",
|
||||
"DIFFERENCE",
|
||||
"DIVIDE",
|
||||
"DST",
|
||||
"EXCLUSION",
|
||||
"HARD_LIGHT",
|
||||
"HARD_MIX",
|
||||
"HUE",
|
||||
"INVERT",
|
||||
"INVERT_RGB",
|
||||
"LIGHTEN",
|
||||
"LINEAR_BURN",
|
||||
"LINEAR_DODGE",
|
||||
"LINEAR_LIGHT",
|
||||
"LUMINOSITY",
|
||||
"MULTIPLY",
|
||||
"NEGATION",
|
||||
"OVERLAY",
|
||||
"PIN_LIGHT",
|
||||
"REFLECT",
|
||||
"SATURATION",
|
||||
"SCREEN",
|
||||
"SET",
|
||||
"SOFT_LIGHT",
|
||||
"SRC",
|
||||
"SUBTRACT",
|
||||
"VIVID_LIGHT",
|
||||
] as const;
|
||||
|
||||
export const blendFunctionOptions = BLEND_FUNCTION_KEYS.map(k => ({ label: k, value: k }));
|
||||
|
||||
// Glitch mode options.
|
||||
export const glitchModeOptions = [
|
||||
{ label: t("layout.sider.postProcessing.Sporadic"), value: "SPORADIC" },
|
||||
{ label: t("layout.sider.postProcessing.Constant Mild"), value: "CONSTANT_MILD" },
|
||||
{ label: t("layout.sider.postProcessing.Constant Wild"), value: "CONSTANT_WILD" },
|
||||
];
|
||||
@ -3,26 +3,111 @@ import {onMounted, ref} from "vue";
|
||||
import { CaretForwardOutline } from "@vicons/ionicons5";
|
||||
import { t } from "@/language";
|
||||
import { App } from "@astral3d/engine";
|
||||
import OutLine from './effect/Sidebar.Effect.Outline.vue';
|
||||
import FXAA from './effect/Sidebar.Effect.FXAA.vue';
|
||||
import UnrealBloom from './effect/Sidebar.Effect.UnrealBloom.vue';
|
||||
import LUT from './effect/Sidebar.Effect.LUT.vue';
|
||||
import Afterimage from './effect/Sidebar.Effect.Afterimage.vue';
|
||||
import Bokeh from './effect/Sidebar.Effect.Bokeh.vue';
|
||||
import Pixelate from './effect/Sidebar.Effect.Pixelate.vue';
|
||||
import Halftone from './effect/Sidebar.Effect.Halftone.vue';
|
||||
import OutLine from "./effect/Sidebar.Effect.Outline.vue";
|
||||
import SMAA from "./effect/Sidebar.Effect.SMAA.vue";
|
||||
import SSAO from "./effect/Sidebar.Effect.SSAO.vue";
|
||||
import UnrealBloom from "./effect/Sidebar.Effect.UnrealBloom.vue";
|
||||
import Bokeh from "./effect/Sidebar.Effect.Bokeh.vue";
|
||||
import Pixelate from "./effect/Sidebar.Effect.Pixelate.vue";
|
||||
import TiltShift from "./effect/Sidebar.Effect.TiltShift.vue";
|
||||
import Scanline from "./effect/Sidebar.Effect.Scanline.vue";
|
||||
import BrightnessContrast from "./effect/Sidebar.Effect.BrightnessContrast.vue";
|
||||
import ChromaticAberration from "./effect/Sidebar.Effect.ChromaticAberration.vue";
|
||||
import ColorDepth from "./effect/Sidebar.Effect.ColorDepth.vue";
|
||||
import Glitch from "./effect/Sidebar.Effect.Glitch.vue";
|
||||
import HueSaturation from "./effect/Sidebar.Effect.HueSaturation.vue";
|
||||
import LensDistortion from "./effect/Sidebar.Effect.LensDistortion.vue";
|
||||
import ShockWave from "./effect/Sidebar.Effect.ShockWave.vue";
|
||||
import ToneMapping from "./effect/Sidebar.Effect.ToneMapping.vue";
|
||||
import Vignette from "./effect/Sidebar.Effect.Vignette.vue";
|
||||
import EsTip from "@/components/es/EsTip.vue";
|
||||
|
||||
const effectDefaults: Record<string, any> = {
|
||||
ToneMapping: { mode: "ACES_FILMIC", exposure: 1.0, blendFunction: "NORMAL" },
|
||||
SMAA: { enabled: true, preset: "ULTRA" },
|
||||
SSAO: {
|
||||
enabled: true,
|
||||
blendFunction: "MULTIPLY",
|
||||
samples: 9,
|
||||
rings: 7,
|
||||
radius: 0.1825,
|
||||
intensity: 1.0,
|
||||
bias: 0.025,
|
||||
fade: 0.01,
|
||||
luminanceInfluence: 0.7,
|
||||
minRadiusScale: 0.1,
|
||||
depthAwareUpsampling: true,
|
||||
resolutionScale: 0.5,
|
||||
distanceThreshold: 0.97,
|
||||
distanceFalloff: 0.03,
|
||||
rangeThreshold: 0.0005,
|
||||
rangeFalloff: 0.001,
|
||||
worldDistanceThreshold: null,
|
||||
worldDistanceFalloff: null,
|
||||
worldProximityThreshold: null,
|
||||
worldProximityFalloff: null,
|
||||
colorEnabled: false,
|
||||
color: "#000000",
|
||||
},
|
||||
TiltShift: { enabled: false, offset: 0.0, rotation: 0.0, focusArea: 0.4, feather: 0.3, blendFunction: "NORMAL" },
|
||||
Scanline: { enabled: false, density: 1.25, scrollSpeed: 0.0, blendFunction: "OVERLAY" },
|
||||
BrightnessContrast: { enabled: false, brightness: 0.0, contrast: 0.0, blendFunction: "SRC" },
|
||||
ChromaticAberration: {
|
||||
enabled: false,
|
||||
offset: { x: 0.002, y: 0.002 },
|
||||
radialModulation: false,
|
||||
modulationOffset: 0.15,
|
||||
blendFunction: "NORMAL",
|
||||
},
|
||||
ColorDepth: { enabled: false, bits: 16, blendFunction: "NORMAL" },
|
||||
Glitch: {
|
||||
enabled: false,
|
||||
chromaticAberrationOffset: null,
|
||||
delay: { min: 1.5, max: 3.5 },
|
||||
duration: { min: 0.6, max: 1.0 },
|
||||
strength: { min: 0.3, max: 1.0 },
|
||||
mode: "SPORADIC",
|
||||
ratio: 0.85,
|
||||
blendFunction: "NORMAL",
|
||||
},
|
||||
HueSaturation: { enabled: false, hue: 0.0, saturation: 0.0, blendFunction: "SRC" },
|
||||
LensDistortion: {
|
||||
enabled: false,
|
||||
distortion: { x: 0.0, y: 0.0 },
|
||||
principalPoint: { x: 0.0, y: 0.0 },
|
||||
focalLength: { x: 1.0, y: 1.0 },
|
||||
skew: 0.0,
|
||||
},
|
||||
ShockWave: { enabled: false, amplitude: 0.05, waveSize: 0.2, speed: 2.0, maxRadius: 1.0, clickTrigger: true },
|
||||
Vignette: { enabled: false, offset: 0.5, darkness: 0.5, blendFunction: "NORMAL" },
|
||||
};
|
||||
|
||||
// 兼容旧项目配置缺失 effect 子项,避免子组件 setup 阶段 JSON.parse(undefined)
|
||||
function ensureEffectDefaults() {
|
||||
if (App.project.getKey("effect") === undefined) {
|
||||
App.project.setKey("effect", { enabled: false }, false);
|
||||
}
|
||||
|
||||
Object.entries(effectDefaults).forEach(([name, config]) => {
|
||||
const key = `effect.${name}`;
|
||||
if (App.project.getKey(key) === undefined) {
|
||||
App.project.setKey(key, JSON.parse(JSON.stringify(config)), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ensureEffectDefaults();
|
||||
|
||||
const effectEnabled = ref(App.project.getKey("effect.enabled"));
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
effectEnabled.value = App.project.getKey("effect.enabled");
|
||||
|
||||
window.viewer.removeEventListener('loaded', viewerLoaded);
|
||||
}
|
||||
window.viewer.addEventListener('loaded', viewerLoaded);
|
||||
})
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleEffectEnabledChange(value: boolean) {
|
||||
App.project.setKey("effect.enabled", value);
|
||||
@ -31,7 +116,7 @@ function handleEffectEnabledChange(value:boolean){
|
||||
|
||||
<template>
|
||||
<div class="flex items-center justify-between">
|
||||
<h4>{{ t('layout.sider.Post processing') }}</h4>
|
||||
<h4>{{ t("layout.sider.Post processing") }}</h4>
|
||||
<n-switch v-model:value="effectEnabled" @update:value="handleEffectEnabledChange">
|
||||
<template #checked>
|
||||
{{ t("other.Open") }}
|
||||
@ -44,7 +129,12 @@ function handleEffectEnabledChange(value:boolean){
|
||||
|
||||
<n-divider class="!my-3" />
|
||||
|
||||
<n-collapse display-directive="show" :default-expanded-names="['Anti-aliasing','Outline','UnrealBloom']">
|
||||
<!-- ToneMapping:色调映射 -->
|
||||
<div class="px-2 mb-3">
|
||||
<ToneMapping :effect-enabled="effectEnabled" />
|
||||
</div>
|
||||
|
||||
<n-collapse display-directive="show" :default-expanded-names="['Anti-aliasing', 'Outline']">
|
||||
<template #arrow>
|
||||
<n-icon>
|
||||
<CaretForwardOutline />
|
||||
@ -53,10 +143,14 @@ function handleEffectEnabledChange(value:boolean){
|
||||
|
||||
<!-- 抗锯齿 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing[\'Anti-aliasing\']')" name="Anti-aliasing">
|
||||
<FXAA :effect-enabled="effectEnabled" />
|
||||
<SMAA :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- Outline:描边线 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.SSAO')" name="SSAO">
|
||||
<SSAO :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Outline')" name="Outline">
|
||||
<OutLine :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
@ -66,40 +160,74 @@ function handleEffectEnabledChange(value:boolean){
|
||||
<UnrealBloom :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- LUT:颜色滤镜 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.LUT Color filter')" name="LUT">
|
||||
<LUT :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- 运动残影 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Afterimage')" name="Afterimage">
|
||||
<Afterimage :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- Bokeh:变焦 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Bokeh')" name="Bokeh">
|
||||
<Bokeh :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- Pixelate:像素风 -->
|
||||
<n-collapse-item name="Pixelate">
|
||||
<n-collapse-item :title="t(`layout.sider.postProcessing.Pixelate`)" name="Pixelate">
|
||||
<Pixelate :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- TiltShift:移轴模糊 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Tilt Shift')" name="TiltShift">
|
||||
<TiltShift :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- Scanline:扫描线 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Scanline')" name="Scanline">
|
||||
<Scanline :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- BrightnessContrast:亮度对比度 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Brightness Contrast')" name="BrightnessContrast">
|
||||
<BrightnessContrast :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- ChromaticAberration:色差 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Chromatic Aberration')" name="ChromaticAberration">
|
||||
<ChromaticAberration :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- ColorDepth:色深 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Color Depth')" name="ColorDepth">
|
||||
<ColorDepth :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- Glitch:故障 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Glitch')" name="Glitch">
|
||||
<Glitch :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- HueSaturation:色相饱和度 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Hue Saturation')" name="HueSaturation">
|
||||
<HueSaturation :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- LensDistortion:镜头畸变 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Lens Distortion')" name="LensDistortion">
|
||||
<LensDistortion :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- ShockWave:冲击波 -->
|
||||
<n-collapse-item name="ShockWave">
|
||||
<template #header>
|
||||
<n-text>
|
||||
<EsTip class="!justify-start" :content="t(`layout.sider.postProcessing.Pixelate`)">
|
||||
{{ t(`layout.sider.postProcessing.Use a solid color background to achieve the best rendering effect`) }}
|
||||
<EsTip class="!justify-start" :content="t(`layout.sider.postProcessing.Shock Wave`)">
|
||||
{{ t(`layout.sider.postProcessing.Click on scene to trigger shock wave effect`) }}
|
||||
</EsTip>
|
||||
</n-text>
|
||||
</template>
|
||||
|
||||
<Pixelate :effect-enabled="effectEnabled" />
|
||||
<ShockWave :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
|
||||
<!-- Halftone:半色调 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Halftoning')" name="Halftoning">
|
||||
<Halftone :effect-enabled="effectEnabled" />
|
||||
<!-- Vignette:晕影 -->
|
||||
<n-collapse-item :title="t('layout.sider.postProcessing.Vignette')" name="Vignette">
|
||||
<Vignette :effect-enabled="effectEnabled" />
|
||||
</n-collapse-item>
|
||||
</n-collapse>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
</style>
|
||||
<style lang="less"></style>
|
||||
@ -28,7 +28,7 @@ function handleAfterimageConfigChange(){
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enable`) }}</span>
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="afterimage.enabled" :disabled="!effectEnabled" @update:checked="handleAfterimageConfigChange"/>
|
||||
</div>
|
||||
@ -46,3 +46,4 @@ function handleAfterimageConfigChange(){
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ function handleBokehConfigChange(){
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enable`) }}</span>
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="bokeh.enabled" :disabled="!effectEnabled" @update:checked="handleBokehConfigChange"/>
|
||||
</div>
|
||||
@ -63,3 +63,4 @@ function handleBokehConfigChange(){
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, toRaw } from "vue";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
import { blendFunctionOptions } from "../../../../../utils/common/postprocessing";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
effectEnabled: boolean;
|
||||
}>(),
|
||||
{
|
||||
effectEnabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
const bc = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.BrightnessContrast"))));
|
||||
const disabled = computed(() => !props.effectEnabled || !bc.enabled);
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(bc, App.project.getKey("effect.BrightnessContrast"));
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleConfigChange() {
|
||||
App.project.setKey(`effect.BrightnessContrast`, toRaw(bc));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="bc.enabled" :disabled="!effectEnabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 亮度 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Brightness`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="bc.brightness" :step="0.01" :min="-1" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 对比度 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Contrast`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="bc.contrast" :step="0.01" :min="-1" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 混合模式 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Blend Function`) }}</span>
|
||||
<div>
|
||||
<n-select size="small" v-model:value="bc.blendFunction" :options="blendFunctionOptions" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
|
||||
@ -0,0 +1,83 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, toRaw } from "vue";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
import { blendFunctionOptions } from "../../../../../utils/common/postprocessing";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
effectEnabled: boolean;
|
||||
}>(),
|
||||
{
|
||||
effectEnabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
const ca = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.ChromaticAberration"))));
|
||||
const disabled = computed(() => !props.effectEnabled || !ca.enabled);
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(ca, App.project.getKey("effect.ChromaticAberration"));
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleConfigChange() {
|
||||
App.project.setKey(`effect.ChromaticAberration`, toRaw(ca));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="ca.enabled" :disabled="!effectEnabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 偏移 X -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Offset X`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ca.offset.x" :step="0.001" :min="0" :max="0.05" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 偏移 Y -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Offset Y`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ca.offset.y" :step="0.001" :min="0" :max="0.05" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 径向调制 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Radial Modulation`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="ca.radialModulation" :disabled="disabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 调制偏移 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Modulation Offset`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ca.modulationOffset" :step="0.01" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 混合模式 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Blend Function`) }}</span>
|
||||
<div>
|
||||
<n-select size="small" v-model:value="ca.blendFunction" :options="blendFunctionOptions" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, toRaw } from "vue";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
import { blendFunctionOptions } from "../../../../../utils/common/postprocessing";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
effectEnabled: boolean;
|
||||
}>(),
|
||||
{
|
||||
effectEnabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
const colorDepth = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.ColorDepth"))));
|
||||
const disabled = computed(() => !props.effectEnabled || !colorDepth.enabled);
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(colorDepth, App.project.getKey("effect.ColorDepth"));
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleConfigChange() {
|
||||
App.project.setKey(`effect.ColorDepth`, toRaw(colorDepth));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="colorDepth.enabled" :disabled="!effectEnabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 色深位数 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Bits`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="colorDepth.bits" :step="1" :min="1" :max="32" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 混合模式 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Blend Function`) }}</span>
|
||||
<div>
|
||||
<n-select v-model:value="colorDepth.blendFunction" :options="blendFunctionOptions" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@ function handleFXAAConfigChange(){
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enable`) }}</span>
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="faxx.enabled" :disabled="!effectEnabled" @update:checked="handleFXAAConfigChange"/>
|
||||
</div>
|
||||
@ -37,3 +37,4 @@ function handleFXAAConfigChange(){
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@ -0,0 +1,121 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, toRaw } from "vue";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
import { blendFunctionOptions, glitchModeOptions } from "../../../../../utils/common/postprocessing";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
effectEnabled: boolean;
|
||||
}>(),
|
||||
{
|
||||
effectEnabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
const glitch = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.Glitch"))));
|
||||
const disabled = computed(() => !props.effectEnabled || !glitch.enabled);
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(glitch, App.project.getKey("effect.Glitch"));
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleConfigChange() {
|
||||
App.project.setKey(`effect.Glitch`, toRaw(glitch));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="glitch.enabled" :disabled="!effectEnabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 故障模式 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Glitch Mode`) }}</span>
|
||||
<div>
|
||||
<n-select v-model:value="glitch.mode" :options="glitchModeOptions" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 延迟最小值 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Delay Min`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="glitch.delay.min" :step="0.1" :min="0" :max="5" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 延迟最大值 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Delay Max`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="glitch.delay.max" :step="0.1" :min="0" :max="10" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 持续时间最小值 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Duration Min`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="glitch.duration.min" :step="0.1" :min="0" :max="2" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 持续时间最大值 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Duration Max`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="glitch.duration.max" :step="0.1" :min="0" :max="5" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 强度最小值 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Strength Min`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="glitch.strength.min" :step="0.1" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 强度最大值 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Strength Max`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="glitch.strength.max" :step="0.1" :min="0" :max="2" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 故障比率 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Ratio`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="glitch.ratio" :step="0.05" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 混合模式 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Blend Function`) }}</span>
|
||||
<div>
|
||||
<n-select
|
||||
size="small"
|
||||
v-model:value="glitch.blendFunction"
|
||||
:options="blendFunctionOptions"
|
||||
:disabled="disabled"
|
||||
@update:value="handleConfigChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ function handleHalftoneConfigChange(){
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enable`) }}</span>
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="halftone.enabled" :disabled="!effectEnabled" @update:checked="handleHalftoneConfigChange"/>
|
||||
</div>
|
||||
@ -121,3 +121,4 @@ function handleHalftoneConfigChange(){
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, toRaw } from "vue";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
import { blendFunctionOptions } from "../../../../../utils/common/postprocessing";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
effectEnabled: boolean;
|
||||
}>(),
|
||||
{
|
||||
effectEnabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
const hueSat = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.HueSaturation"))));
|
||||
const disabled = computed(() => !props.effectEnabled || !hueSat.enabled);
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(hueSat, App.project.getKey("effect.HueSaturation"));
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleConfigChange() {
|
||||
App.project.setKey(`effect.HueSaturation`, toRaw(hueSat));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="hueSat.enabled" :disabled="!effectEnabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 色相 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Hue`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="hueSat.hue" :step="0.01" :min="-3.14" :max="3.14" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 饱和度 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Saturation`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="hueSat.saturation" :step="0.01" :min="-1" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 混合模式 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Blend Function`) }}</span>
|
||||
<div>
|
||||
<n-select v-model:value="hueSat.blendFunction" :options="blendFunctionOptions" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ function handleLUTConfigChange(){
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enable`) }}</span>
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="lut.enabled" :disabled="!effectEnabled" @update:checked="handleLUTConfigChange"/>
|
||||
</div>
|
||||
@ -56,3 +56,4 @@ function handleLUTConfigChange(){
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@ -0,0 +1,98 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, toRaw } from "vue";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
effectEnabled: boolean;
|
||||
}>(),
|
||||
{
|
||||
effectEnabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
const lens = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.LensDistortion"))));
|
||||
const disabled = computed(() => !props.effectEnabled || !lens.enabled);
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(lens, App.project.getKey("effect.LensDistortion"));
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleConfigChange() {
|
||||
App.project.setKey(`effect.LensDistortion`, toRaw(lens));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="lens.enabled" :disabled="!effectEnabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 畸变 X -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Distortion X`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="lens.distortion.x" :step="0.01" :min="-1" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 畸变 Y -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Distortion Y`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="lens.distortion.y" :step="0.01" :min="-1" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主点 X -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Principal Point X`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="lens.principalPoint.x" :step="0.01" :min="-1" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主点 Y -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Principal Point Y`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="lens.principalPoint.y" :step="0.01" :min="-1" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 焦距 X -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Focal Length X`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="lens.focalLength.x" :step="0.1" :min="0.1" :max="5" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 焦距 Y -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Focal Length Y`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="lens.focalLength.y" :step="0.1" :min="0.1" :max="5" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 倾斜 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Skew`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="lens.skew" :step="0.01" :min="-1" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ function handleOutlineConfigChange(){
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enable`) }}</span>
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="outline.enabled" :disabled="!effectEnabled" @update:checked="handleOutlineConfigChange"/>
|
||||
</div>
|
||||
@ -86,3 +86,4 @@ function handleOutlineConfigChange(){
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ function handlePixelateConfigChange(){
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enable`) }}</span>
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="pixelate.enabled" :disabled="!effectEnabled" @update:checked="handlePixelateConfigChange"/>
|
||||
</div>
|
||||
@ -62,3 +62,4 @@ function handlePixelateConfigChange(){
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, toRaw } from "vue";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
effectEnabled: boolean;
|
||||
}>(),
|
||||
{
|
||||
effectEnabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
// SMAA (Subpixel Morphological Anti-Aliasing) 替代 FXAA
|
||||
const smaa = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.SMAA"))));
|
||||
const disabled = computed(() => !props.effectEnabled || !smaa.enabled);
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(smaa, App.project.getKey("effect.SMAA"));
|
||||
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleSMAAConfigChange() {
|
||||
App.project.setKey(`effect.SMAA`, toRaw(smaa));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="smaa.enabled" :disabled="!effectEnabled" @update:checked="handleSMAAConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 质量预设 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Quality Preset`) }}</span>
|
||||
<div>
|
||||
<n-select
|
||||
v-model:value="smaa.preset"
|
||||
:disabled="disabled"
|
||||
@update:value="handleSMAAConfigChange"
|
||||
:options="[
|
||||
{ label: 'Low', value: 'LOW' },
|
||||
{ label: 'Medium', value: 'MEDIUM' },
|
||||
{ label: 'High', value: 'HIGH' },
|
||||
{ label: 'Ultra', value: 'ULTRA' },
|
||||
]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
|
||||
@ -0,0 +1,208 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, toRaw } from "vue";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
import { blendFunctionOptions } from "../../../../../utils/common/postprocessing";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
effectEnabled: boolean;
|
||||
}>(),
|
||||
{
|
||||
effectEnabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
const ssao = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.SSAO"))));
|
||||
const disabled = computed(() => !props.effectEnabled || !ssao.enabled);
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(ssao, App.project.getKey("effect.SSAO"));
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
// 世界空间阈值允许为空,这里在提交前归一化,避免 NaN 污染配置
|
||||
function normalizeOptionalNumber(value: unknown): number | null {
|
||||
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
||||
}
|
||||
|
||||
function handleConfigChange() {
|
||||
ssao.worldDistanceThreshold = normalizeOptionalNumber(ssao.worldDistanceThreshold);
|
||||
ssao.worldDistanceFalloff = normalizeOptionalNumber(ssao.worldDistanceFalloff);
|
||||
ssao.worldProximityThreshold = normalizeOptionalNumber(ssao.worldProximityThreshold);
|
||||
ssao.worldProximityFalloff = normalizeOptionalNumber(ssao.worldProximityFalloff);
|
||||
|
||||
App.project.setKey("effect.SSAO", toRaw(ssao));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="ssao.enabled" :disabled="!effectEnabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Blend Function`) }}</span>
|
||||
<div>
|
||||
<n-select size="small" v-model:value="ssao.blendFunction" :options="blendFunctionOptions" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Samples`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.samples" :step="1" :min="1" :max="64" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Rings`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.rings" :step="1" :min="1" :max="32" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Radius`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.radius" :step="0.001" :min="0.01" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Strength`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.intensity" :step="0.01" :min="0" :max="5" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Bias`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.bias" :step="0.001" :min="0" :max="0.1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Fade`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.fade" :step="0.001" :min="0" :max="0.1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Luminance Influence`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.luminanceInfluence" :step="0.01" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Min Radius Scale`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.minRadiusScale" :step="0.01" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Depth-aware Upsampling`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="ssao.depthAwareUpsampling" :disabled="disabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Resolution Scale`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.resolutionScale" :step="0.05" :min="0.1" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Distance Threshold`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.distanceThreshold" :step="0.001" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Distance Falloff`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.distanceFalloff" :step="0.001" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Range Threshold`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.rangeThreshold" :step="0.0001" :min="0" :max="0.02" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Range Falloff`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="ssao.rangeFalloff" :step="0.0001" :min="0" :max="0.02" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.World Distance Threshold`) }}</span>
|
||||
<div>
|
||||
<n-input-number v-model:value="ssao.worldDistanceThreshold" size="small" :min="0" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.World Distance Falloff`) }}</span>
|
||||
<div>
|
||||
<n-input-number v-model:value="ssao.worldDistanceFalloff" size="small" :min="0" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.World Proximity Threshold`) }}</span>
|
||||
<div>
|
||||
<n-input-number v-model:value="ssao.worldProximityThreshold" size="small" :min="0" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.World Proximity Falloff`) }}</span>
|
||||
<div>
|
||||
<n-input-number v-model:value="ssao.worldProximityFalloff" size="small" :min="0" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Colorize`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="ssao.colorEnabled" :disabled="disabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.AO Color`) }}</span>
|
||||
<div>
|
||||
<n-color-picker
|
||||
v-model:value="ssao.color"
|
||||
:show-alpha="false"
|
||||
:modes="['hex']"
|
||||
size="small"
|
||||
:disabled="disabled || !ssao.colorEnabled"
|
||||
@update:value="handleConfigChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, toRaw } from "vue";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
import { blendFunctionOptions } from "../../../../../utils/common/postprocessing";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
effectEnabled: boolean;
|
||||
}>(),
|
||||
{
|
||||
effectEnabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
const scanline = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.Scanline"))));
|
||||
const disabled = computed(() => !props.effectEnabled || !scanline.enabled);
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(scanline, App.project.getKey("effect.Scanline"));
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleConfigChange() {
|
||||
App.project.setKey(`effect.Scanline`, toRaw(scanline));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="scanline.enabled" :disabled="!effectEnabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 密度 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Density`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="scanline.density" :step="0.05" :min="0.1" :max="5" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 滚动速度 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Scroll Speed`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="scanline.scrollSpeed" :step="0.01" :min="0" :max="2" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 混合模式 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Blend Function`) }}</span>
|
||||
<div>
|
||||
<n-select
|
||||
size="small"
|
||||
v-model:value="scanline.blendFunction"
|
||||
:options="blendFunctionOptions"
|
||||
:disabled="disabled"
|
||||
@update:value="handleConfigChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,91 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, toRaw } from "vue";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
effectEnabled: boolean;
|
||||
}>(),
|
||||
{
|
||||
effectEnabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
const shockWave = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.ShockWave"))));
|
||||
const disabled = computed(() => !props.effectEnabled || !shockWave.enabled);
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(shockWave, App.project.getKey("effect.ShockWave"));
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleConfigChange() {
|
||||
App.project.setKey(`effect.ShockWave`, toRaw(shockWave));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="shockWave.enabled" :disabled="!effectEnabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 振幅 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Amplitude`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="shockWave.amplitude" :step="0.01" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 波浪大小 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Wave Size`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="shockWave.waveSize" :step="0.01" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 速度 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Speed`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="shockWave.speed" :step="0.1" :min="0.1" :max="10" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最大半径 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Max Radius`) }}</span>
|
||||
<div>
|
||||
<EsInputNumber
|
||||
v-model:value="shockWave.maxRadius"
|
||||
:show-button="false"
|
||||
:decimal="1"
|
||||
:min="0"
|
||||
:max="100"
|
||||
size="small"
|
||||
:disabled="disabled"
|
||||
@change="handleConfigChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 点击触发 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Click Trigger`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="shockWave.clickTrigger" :disabled="disabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
|
||||
@ -0,0 +1,91 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, toRaw } from "vue";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
import { blendFunctionOptions } from "../../../../../utils/common/postprocessing";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
effectEnabled: boolean;
|
||||
}>(),
|
||||
{
|
||||
effectEnabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
const tiltShift = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.TiltShift"))));
|
||||
const disabled = computed(() => !props.effectEnabled || !tiltShift.enabled);
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(tiltShift, App.project.getKey("effect.TiltShift"));
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleConfigChange() {
|
||||
App.project.setKey(`effect.TiltShift`, toRaw(tiltShift));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="tiltShift.enabled" :disabled="!effectEnabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 焦点偏移 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Offset`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="tiltShift.offset" :step="0.01" :min="-1" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 旋转角度 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Rotation`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="tiltShift.rotation" :step="0.01" :min="0" :max="6.28" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 焦点区域 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Focus Area`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="tiltShift.focusArea" :step="0.01" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 羽化 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Feather`) }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="tiltShift.feather" :step="0.01" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 混合模式 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Blend Function`) }}</span>
|
||||
<div>
|
||||
<n-select
|
||||
size="small"
|
||||
v-model:value="tiltShift.blendFunction"
|
||||
:options="blendFunctionOptions"
|
||||
:disabled="disabled"
|
||||
@update:value="handleConfigChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,81 @@
|
||||
<script setup lang="ts">
|
||||
import { h, onMounted, reactive, toRaw, VNode } from "vue";
|
||||
import { SelectOption, NTooltip } from "naive-ui";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
import { blendFunctionOptions } from "../../../../../utils/common/postprocessing";
|
||||
|
||||
const defaultToneMapping = {
|
||||
mode: "ACES_FILMIC",
|
||||
exposure: 1,
|
||||
blendFunction: "NORMAL",
|
||||
};
|
||||
|
||||
const toneMapping = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.ToneMapping") || defaultToneMapping)));
|
||||
|
||||
// 色调映射模式选项
|
||||
const toneMappingModeOptions = [
|
||||
{ label: "Linear", value: "LINEAR", tooltip: "线性映射,保持原始颜色" },
|
||||
{ label: "Reinhard", value: "REINHARD", tooltip: "Reinhard 映射,柔和的高光压缩" },
|
||||
{ label: "Reinhard2", value: "REINHARD2", tooltip: "改进的 Reinhard 映射" },
|
||||
{ label: "Optimized Cineon", value: "OPTIMIZED_CINEON", tooltip: "优化的电影风格映射" },
|
||||
{ label: "ACES Filmic", value: "ACES_FILMIC", tooltip: "电影工业标准 ACES 映射" },
|
||||
{ label: "AgX", value: "AGX", tooltip: "AgX 映射,适合户外场景" },
|
||||
{ label: "Neutral", value: "NEUTRAL", tooltip: "中性映射,平衡的色调" },
|
||||
];
|
||||
|
||||
const renderToneMappingOption = ({ node, option }: { node: VNode; option: SelectOption }) => {
|
||||
return h(NTooltip, null, {
|
||||
trigger: () => node,
|
||||
default: () => (option as any).tooltip || option.label,
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(toneMapping, App.project.getKey("effect.ToneMapping") || defaultToneMapping);
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleConfigChange() {
|
||||
App.project.setKey(`effect.ToneMapping`, toRaw(toneMapping));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 色调映射模式 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.rendererConfig.Tone mapping`) || "色调映射" }}</span>
|
||||
<div>
|
||||
<n-select
|
||||
v-model:value="toneMapping.mode"
|
||||
:options="toneMappingModeOptions"
|
||||
:render-option="renderToneMappingOption"
|
||||
size="small"
|
||||
@update:value="handleConfigChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 曝光度 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Exposure`) || "曝光度" }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="toneMapping.exposure" :step="0.01" :min="0.1" :max="3" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 混合模式 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Blend Function`) }}</span>
|
||||
<div>
|
||||
<n-select size="small" v-model:value="toneMapping.blendFunction" :options="blendFunctionOptions" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ function handleUnrealBloomConfigChange(){
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enable`) }}</span>
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="unrealBloom.enabled" :disabled="!effectEnabled" @update:checked="handleUnrealBloomConfigChange"/>
|
||||
</div>
|
||||
@ -62,3 +62,4 @@ function handleUnrealBloomConfigChange(){
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, toRaw } from "vue";
|
||||
import { t } from "@/language";
|
||||
import { App, Utils } from "@astral3d/engine";
|
||||
import { blendFunctionOptions } from "../../../../../utils/common/postprocessing";
|
||||
|
||||
defineProps<{
|
||||
effectEnabled: boolean;
|
||||
}>();
|
||||
|
||||
const vignette = reactive(JSON.parse(JSON.stringify(App.project.getKey("effect.Vignette"))));
|
||||
const disabled = computed(() => !vignette.enabled);
|
||||
|
||||
onMounted(() => {
|
||||
const viewerLoaded = () => {
|
||||
Utils.deepAssign(vignette, App.project.getKey("effect.Vignette"));
|
||||
window.viewer.removeEventListener("loaded", viewerLoaded);
|
||||
};
|
||||
window.viewer.addEventListener("loaded", viewerLoaded);
|
||||
});
|
||||
|
||||
function handleConfigChange() {
|
||||
App.project.setKey(`effect.Vignette`, toRaw(vignette));
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`other.Enabled`) }}</span>
|
||||
<div>
|
||||
<n-checkbox size="small" v-model:checked="vignette.enabled" @update:checked="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 偏移 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t("layout.sider.postProcessing.Offset") }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="vignette.offset" :step="0.01" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 暗度 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t("layout.sider.postProcessing.Darkness") }}</span>
|
||||
<div>
|
||||
<n-slider v-model:value="vignette.darkness" :step="0.01" :min="0" :max="1" :disabled="disabled" @update:value="handleConfigChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 混合模式 -->
|
||||
<div class="sidebar-config-item">
|
||||
<span>{{ t(`layout.sider.postProcessing.Blend Function`) }}</span>
|
||||
<div>
|
||||
<n-select
|
||||
size="small"
|
||||
v-model:value="vignette.blendFunction"
|
||||
:options="blendFunctionOptions"
|
||||
:disabled="disabled"
|
||||
@update:value="handleConfigChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less"></style>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -41,6 +41,46 @@ export const defaultProjectInfo = (): IAppProject.Info => ({
|
||||
// 后处理
|
||||
effect: {
|
||||
enabled: false,
|
||||
// 色调映射 (ToneMappingEffect)
|
||||
ToneMapping: {
|
||||
// 色调映射模式:LINEAR, REINHARD, REINHARD2, OPTIMIZED_CINEON, ACES_FILMIC, AGX, NEUTRAL
|
||||
mode: "ACES_FILMIC",
|
||||
// 曝光度
|
||||
exposure: 1.0,
|
||||
// 混合模式
|
||||
blendFunction: "NORMAL"
|
||||
},
|
||||
// 抗锯齿 (SMAAEffect)
|
||||
SMAA: {
|
||||
enabled: true,
|
||||
// 质量预设: LOW, MEDIUM, HIGH, ULTRA
|
||||
preset: "ULTRA"
|
||||
},
|
||||
// 环境光遮蔽 (SSAOEffect)
|
||||
SSAO: {
|
||||
enabled: true,
|
||||
blendFunction: "MULTIPLY",
|
||||
samples: 9,
|
||||
rings: 7,
|
||||
radius: 0.1825,
|
||||
intensity: 1.0,
|
||||
bias: 0.025,
|
||||
fade: 0.01,
|
||||
luminanceInfluence: 0.7,
|
||||
minRadiusScale: 0.1,
|
||||
depthAwareUpsampling: true,
|
||||
resolutionScale: 0.5,
|
||||
distanceThreshold: 0.97,
|
||||
distanceFalloff: 0.03,
|
||||
rangeThreshold: 0.0005,
|
||||
rangeFalloff: 0.001,
|
||||
worldDistanceThreshold: null,
|
||||
worldDistanceFalloff: null,
|
||||
worldProximityThreshold: null,
|
||||
worldProximityFalloff: null,
|
||||
colorEnabled: false,
|
||||
color: "#000000",
|
||||
},
|
||||
// 描边线
|
||||
Outline: {
|
||||
enabled: true,
|
||||
@ -57,7 +97,14 @@ export const defaultProjectInfo = (): IAppProject.Info => ({
|
||||
// 可见边缘的颜色
|
||||
visibleEdgeColor: "#ffee00",
|
||||
// 不可见边缘的颜色
|
||||
hiddenEdgeColor: "#ff6a00"
|
||||
hiddenEdgeColor: "#ff6a00",
|
||||
// Astral postprocessing 兼容字段
|
||||
pulseSpeed: 0.0,
|
||||
xRay: true,
|
||||
blur: true,
|
||||
kernelSize: 1,
|
||||
multisampling: 4,
|
||||
blendFunction: "SCREEN"
|
||||
},
|
||||
// 抗锯齿
|
||||
FXAA: {
|
||||
@ -71,7 +118,14 @@ export const defaultProjectInfo = (): IAppProject.Info => ({
|
||||
// 光晕强度
|
||||
strength: 1,
|
||||
// 光晕半径
|
||||
radius: 0
|
||||
radius: 0,
|
||||
// Astral postprocessing 兼容字段
|
||||
intensity: 1.0,
|
||||
luminanceThreshold: 0.9,
|
||||
luminanceSmoothing: 0.025,
|
||||
levels: 8,
|
||||
mipmapBlur: true,
|
||||
blendFunction: "SCREEN"
|
||||
},
|
||||
// 背景虚化
|
||||
Bokeh: {
|
||||
@ -81,7 +135,12 @@ export const defaultProjectInfo = (): IAppProject.Info => ({
|
||||
// 孔径,类似相机孔径调节
|
||||
aperture: 0.00005,
|
||||
// 最大模糊程度
|
||||
maxblur: 0.01
|
||||
maxblur: 0.01,
|
||||
// Astral postprocessing 兼容字段
|
||||
focusDistance: 10.0,
|
||||
focusRange: 5.0,
|
||||
bokehScale: 2.0,
|
||||
resolutionScale: 0.5
|
||||
},
|
||||
// 像素风
|
||||
Pixelate: {
|
||||
@ -92,6 +151,87 @@ export const defaultProjectInfo = (): IAppProject.Info => ({
|
||||
normalEdgeStrength: 0.3,
|
||||
// 深度边缘强度
|
||||
depthEdgeStrength: 0.4,
|
||||
// Astral postprocessing 兼容字段
|
||||
granularity: 6,
|
||||
},
|
||||
// 移轴模糊 (TiltShiftEffect)
|
||||
TiltShift: {
|
||||
enabled: false,
|
||||
offset: 0.0,
|
||||
rotation: 0.0,
|
||||
focusArea: 0.4,
|
||||
feather: 0.3,
|
||||
blendFunction: "NORMAL"
|
||||
},
|
||||
// 扫描线 (ScanlineEffect)
|
||||
Scanline: {
|
||||
enabled: false,
|
||||
density: 1.25,
|
||||
scrollSpeed: 0.0,
|
||||
blendFunction: "OVERLAY"
|
||||
},
|
||||
// 亮度对比度 (BrightnessContrastEffect)
|
||||
BrightnessContrast: {
|
||||
enabled: false,
|
||||
brightness: 0.0,
|
||||
contrast: 0.0,
|
||||
blendFunction: "SRC"
|
||||
},
|
||||
// 色差 (ChromaticAberrationEffect)
|
||||
ChromaticAberration: {
|
||||
enabled: false,
|
||||
offset: { x: 0.002, y: 0.002 },
|
||||
radialModulation: false,
|
||||
modulationOffset: 0.15,
|
||||
blendFunction: "NORMAL"
|
||||
},
|
||||
// 色深 (ColorDepthEffect)
|
||||
ColorDepth: {
|
||||
enabled: false,
|
||||
bits: 16,
|
||||
blendFunction: "NORMAL"
|
||||
},
|
||||
// 故障 (GlitchEffect)
|
||||
Glitch: {
|
||||
enabled: false,
|
||||
chromaticAberrationOffset: null,
|
||||
delay: { min: 1.5, max: 3.5 },
|
||||
duration: { min: 0.6, max: 1.0 },
|
||||
strength: { min: 0.3, max: 1.0 },
|
||||
mode: "SPORADIC",
|
||||
ratio: 0.85,
|
||||
blendFunction: "NORMAL"
|
||||
},
|
||||
// 色相饱和度 (HueSaturationEffect)
|
||||
HueSaturation: {
|
||||
enabled: false,
|
||||
hue: 0.0,
|
||||
saturation: 0.0,
|
||||
blendFunction: "SRC"
|
||||
},
|
||||
// 镜头畸变 (LensDistortionEffect)
|
||||
LensDistortion: {
|
||||
enabled: false,
|
||||
distortion: { x: 0.0, y: 0.0 },
|
||||
principalPoint: { x: 0.0, y: 0.0 },
|
||||
focalLength: { x: 1.0, y: 1.0 },
|
||||
skew: 0.0
|
||||
},
|
||||
// 冲击波 (ShockWaveEffect)
|
||||
ShockWave: {
|
||||
enabled: false,
|
||||
amplitude: 0.05,
|
||||
waveSize: 0.2,
|
||||
speed: 2.0,
|
||||
maxRadius: 1.0,
|
||||
clickTrigger: true
|
||||
},
|
||||
// 暗角 (VignetteEffect)
|
||||
Vignette: {
|
||||
enabled: false,
|
||||
offset: 0.5,
|
||||
darkness: 0.5,
|
||||
blendFunction: "NORMAL"
|
||||
},
|
||||
// 半色调
|
||||
Halftone: {
|
||||
@ -223,7 +363,12 @@ class Project {
|
||||
* @param {string} key 可以多层级,需用.分割,如a.b.c
|
||||
*/
|
||||
getKey(key: string): any {
|
||||
return getNestedProperty(this.info, key);
|
||||
const value = getNestedProperty(this.info, key);
|
||||
if (value !== undefined) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return getNestedProperty(defaultProjectInfo(), key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user