diff --git a/js/config/config-manager.js b/js/config/config-manager.js
index fb4b659..9abcfff 100644
--- a/js/config/config-manager.js
+++ b/js/config/config-manager.js
@@ -116,7 +116,9 @@ let defaultConfig = {
'TEST_SAFARI_ENTRY': false,
'UI_THEME': "OpenLive3D",
'RENDER_RESOLUTION': 1.0,
- 'AA_MODE': 'None (Fastest)'
+ 'AA_MODE': 'None (Fastest)',
+ 'UPSCALING_PRESET': 'Ultra Quality (0.77x)',
+ 'FSR_SHARPNESS': 0.2,
};
function getDefaultCMV(key) {
@@ -335,7 +337,7 @@ function getBinaryCM() {
}
function getSelectCM() {
- return ['LANGUAGE', 'TRACKING_MODE', 'UI_THEME', 'AA_MODE'];
+ return ['LANGUAGE', 'TRACKING_MODE', 'UI_THEME', 'AA_MODE', 'UPSCALING_PRESET'];
}
function getSideBoxes() {
@@ -407,8 +409,32 @@ function getConfigModifiers() {
'RENDERING': [{
'key': 'AA_MODE',
'title': 'Anti-Aliasing',
- 'describe': 'Select anti-aliasing mode. FXAA is a fast shader-based filter. MSAA uses hardware multi-sampling via render targets. Combine both for best quality.',
- 'valid': ['None (Fastest)', 'FXAA (Fast)', 'MSAA 4x (Balanced)', 'MSAA 4x + FXAA (Quality)']
+ 'describe': 'Select anti-aliasing and FSR upscaling.',
+ 'valid': [
+ 'None (Fastest)',
+ 'FXAA (Fast)',
+ 'MSAA 4x (Balanced)',
+ 'MSAA 4x + FXAA (Quality)',
+ 'FSR 1.0',
+ 'FXAA + FSR 1.0',
+ 'MSAA 4x + FSR 1.0'
+ ]
+ }, {
+ 'key': 'UPSCALING_PRESET',
+ 'title': 'FSR Quality',
+ 'describe': 'FSR Resolution Presets. Custom allows manual scaling.',
+ 'valid': [
+ 'Ultra Quality (0.77x)',
+ 'Quality (0.67x)',
+ 'Balanced (0.59x)',
+ 'Performance (0.50x)',
+ 'Custom'
+ ]
+ }, {
+ 'key': 'FSR_SHARPNESS',
+ 'title': 'FSR Sharpness',
+ 'describe': 'FSR RCAS Sharpness. 0 = max, 2 = none.',
+ 'range': [0, 2]
}, {
'key': 'RENDER_RESOLUTION',
'title': 'Render Resolution',
diff --git a/js/effect/fsr.js b/js/effect/fsr.js
new file mode 100644
index 0000000..87b4ee2
--- /dev/null
+++ b/js/effect/fsr.js
@@ -0,0 +1,169 @@
+// ol3dc/js/effect/fsr.js
+
+const fsrVertexShader = `#version 300 es
+in vec3 position;
+in vec2 uv;
+out vec2 vUv;
+void main() {
+ vUv = uv;
+ gl_Position = vec4(position, 1.0);
+}`;
+
+let fsrEASUMaterial = null;
+let fsrRCASMaterial = null;
+
+const fsrEASUFragmentShader = `#version 300 es
+precision highp float;
+uniform sampler2D tDiffuse;
+uniform vec2 resolution; // Final display resolution
+uniform vec2 renderResolution;// Internal 3D render resolution
+in vec2 vUv;
+out vec4 fragColor;
+
+// EASU logic heavily optimized for WebGL2
+void main() {
+ vec2 inputSize = renderResolution;
+ vec2 outputSize = resolution;
+
+ vec2 fragCoord = vUv * outputSize;
+ vec2 pp = fragCoord * (inputSize / outputSize) - vec2(0.5);
+ vec2 fp = floor(pp);
+ vec2 f = pp - fp;
+
+ vec2 p0 = (fp + vec2(0.5)) / inputSize;
+ vec2 d = vec2(1.0) / inputSize;
+
+ // Sample 12-tap window
+ vec3 bC = texture(tDiffuse, p0 + vec2(0.0, -1.0) * d).rgb;
+ vec3 cC = texture(tDiffuse, p0 + vec2(1.0, -1.0) * d).rgb;
+ vec3 dC = texture(tDiffuse, p0 + vec2(-1.0, 0.0) * d).rgb;
+ vec3 eC = texture(tDiffuse, p0 + vec2(0.0, 0.0) * d).rgb;
+ vec3 fC = texture(tDiffuse, p0 + vec2(1.0, 0.0) * d).rgb;
+ vec3 gC = texture(tDiffuse, p0 + vec2(2.0, 0.0) * d).rgb;
+ vec3 hC = texture(tDiffuse, p0 + vec2(-1.0, 1.0) * d).rgb;
+ vec3 iC = texture(tDiffuse, p0 + vec2(0.0, 1.0) * d).rgb;
+ vec3 jC = texture(tDiffuse, p0 + vec2(1.0, 1.0) * d).rgb;
+ vec3 kC = texture(tDiffuse, p0 + vec2(2.0, 1.0) * d).rgb;
+ vec3 lC = texture(tDiffuse, p0 + vec2(0.0, 2.0) * d).rgb;
+ vec3 mC = texture(tDiffuse, p0 + vec2(1.0, 2.0) * d).rgb;
+
+ // Luma approximation
+ float bL = bC.g + 0.5 * (bC.r + bC.b);
+ float cL = cC.g + 0.5 * (cC.r + cC.b);
+ float dL = dC.g + 0.5 * (dC.r + dC.b);
+ float eL = eC.g + 0.5 * (eC.r + eC.b);
+ float fL = fC.g + 0.5 * (fC.r + fC.b);
+ float gL = gC.g + 0.5 * (gC.r + gC.b);
+ float hL = hC.g + 0.5 * (hC.r + hC.b);
+ float iL = iC.g + 0.5 * (iC.r + iC.b);
+ float jL = jC.g + 0.5 * (jC.r + jC.b);
+ float kL = kC.g + 0.5 * (kC.r + kC.b);
+ float lL = lC.g + 0.5 * (lC.r + lC.b);
+ float mL = mC.g + 0.5 * (mC.r + mC.b);
+
+ // Gradients
+ float dirX = -bL + cL - eL + fL - iL + jL;
+ float dirY = -dL - eL - hL + fL + gL + jL;
+ vec2 dir = vec2(dirX, dirY);
+
+ float len = length(dir);
+ if (len > 0.0) dir /= len;
+
+ float weight = clamp(len / max(max(eL, fL), max(iL, jL)), 0.0, 1.0);
+ weight *= weight;
+
+ // Bilinear fallback for non-edge
+ vec3 color = eC * (1.0 - f.x) * (1.0 - f.y) + fC * f.x * (1.0 - f.y) + iC * (1.0 - f.x) * f.y + jC * f.x * f.y;
+
+ // Edge reconstruction (simplified to save GPU bandwidth)
+ color = mix(color, (eC + fC + iC + jC) * 0.25, weight * 0.5);
+
+ float a = texture(tDiffuse, vUv).a;
+ fragColor = vec4(color, a);
+} `;
+
+const fsrRCASFragmentShader = `#version 300 es
+precision highp float;
+uniform sampler2D tDiffuse;
+uniform vec2 resolution;
+uniform float sharpness;
+in vec2 vUv;
+out vec4 fragColor;
+
+vec3 linearToSRGB(vec3 color) {
+ return mix(color * 12.92, 1.055 * pow(color, vec3(1.0 / 2.4)) - 0.055, step(0.0031308, color));
+}
+
+// RCAS logic heavily optimized for WebGL2
+void main() {
+ vec2 d = vec2(1.0) / resolution;
+
+ vec3 b = texture(tDiffuse, vUv + vec2( 0.0, -1.0) * d).rgb;
+ vec3 dC = texture(tDiffuse, vUv + vec2(-1.0, 0.0) * d).rgb;
+ vec3 e = texture(tDiffuse, vUv).rgb;
+ vec3 f = texture(tDiffuse, vUv + vec2( 1.0, 0.0) * d).rgb;
+ vec3 h = texture(tDiffuse, vUv + vec2( 0.0, 1.0) * d).rgb;
+
+ float bL = b.g + 0.5 * (b.r + b.b);
+ float dL = dC.g + 0.5 * (dC.r + dC.b);
+ float eL = e.g + 0.5 * (e.r + e.b);
+ float fL = f.g + 0.5 * (f.r + f.b);
+ float hL = h.g + 0.5 * (h.r + h.b);
+
+ float minL = min(eL, min(min(bL, dL), min(fL, hL)));
+ float maxL = max(eL, max(max(bL, dL), max(fL, hL)));
+ float limit = min(eL - minL, maxL - eL);
+
+ float w = limit / (maxL + 1e-5);
+ w = clamp(w, 0.0, 1.0);
+
+ float s = exp2(-sharpness);
+ w = w * s * 0.5;
+
+ vec3 color = (b + dC + f + h) * (-w) + e;
+ color /= (1.0 - 4.0 * w);
+
+ // Convert to sRGB to fix washed-out colors
+ color = linearToSRGB(color);
+
+ float a = texture(tDiffuse, vUv).a;
+ fragColor = vec4(clamp(color, 0.0, 1.0), a);
+}`;
+
+function setupFSR(displayW, displayH, renderW, renderH) {
+ if (!fsrEASUMaterial) {
+ // Use RawShaderMaterial because we are using raw #version 300 es GLSL3
+ fsrEASUMaterial = new THREE.RawShaderMaterial({
+ uniforms: {
+ tDiffuse: { value: null },
+ resolution: { value: new THREE.Vector2(displayW, displayH) },
+ renderResolution: { value: new THREE.Vector2(renderW, renderH) }
+ },
+ vertexShader: fsrVertexShader,
+ fragmentShader: fsrEASUFragmentShader,
+ depthTest: false,
+ depthWrite: false,
+ blending: THREE.NoBlending
+ });
+ } else {
+ fsrEASUMaterial.uniforms.resolution.value.set(displayW, displayH);
+ fsrEASUMaterial.uniforms.renderResolution.value.set(renderW, renderH);
+ }
+
+ if (!fsrRCASMaterial) {
+ fsrRCASMaterial = new THREE.RawShaderMaterial({
+ uniforms: {
+ tDiffuse: { value: null },
+ resolution: { value: new THREE.Vector2(displayW, displayH) },
+ sharpness: { value: 0.2 }
+ },
+ vertexShader: fsrVertexShader,
+ fragmentShader: fsrRCASFragmentShader,
+ depthTest: false,
+ depthWrite: false,
+ blending: THREE.NoBlending
+ });
+ } else {
+ fsrRCASMaterial.uniforms.resolution.value.set(displayW, displayH);
+ }
+}
\ No newline at end of file
diff --git a/js/gui/gui-layout.js b/js/gui/gui-layout.js
index 74278ca..ae35d3d 100644
--- a/js/gui/gui-layout.js
+++ b/js/gui/gui-layout.js
@@ -120,12 +120,69 @@ function onWindowResize() {
backgroundeffect.height = window.innerHeight;
}
+function getRenderResRatio() {
+ let resRatio = getCMV("RENDER_RESOLUTION") || 1.0;
+ let aaMode = getCMV("AA_MODE") || "";
+ if (aaMode.includes("FSR")) {
+ let preset = getCMV("UPSCALING_PRESET") || "Ultra Quality (0.77x)";
+ if (preset === "Ultra Quality (0.77x)") resRatio = 0.77;
+ else if (preset === "Quality (0.67x)") resRatio = 0.67;
+ else if (preset === "Balanced (0.59x)") resRatio = 0.59;
+ else if (preset === "Performance (0.50x)") resRatio = 0.50;
+ }
+ return resRatio;
+}
+
+function updateRenderingUI() {
+ let aaMode = getCMV("AA_MODE") || "";
+ let useFSR = aaMode.includes("FSR");
+ let preset = getCMV("UPSCALING_PRESET") || "Custom";
+
+ let toggleVisibility = (key, show) => {
+ let box = document.getElementById(key + "_box");
+ if (!box) return;
+
+ let isSelect = getSelectCM().includes(key);
+ let nextEl = box.nextElementSibling;
+
+ if (isSelect) {
+ box.style.display = "none"; // Keep dummy input hidden
+ if (nextEl && nextEl.tagName === 'SELECT') {
+ nextEl.style.display = show ? "" : "none";
+ let finalBr = nextEl.nextElementSibling;
+ if (finalBr && finalBr.tagName === 'BR') finalBr.style.display = show ? "" : "none";
+ }
+ } else {
+ box.style.display = show ? "" : "none"; // Range slider
+ if (nextEl && nextEl.tagName === 'INPUT') {
+ nextEl.style.display = show ? "inline-block" : "none";
+ let finalBr = nextEl.nextElementSibling;
+ if (finalBr && finalBr.tagName === 'BR') finalBr.style.display = show ? "" : "none";
+ }
+ }
+
+ let br1 = box.previousElementSibling;
+ if (br1 && br1.tagName === 'BR') br1.style.display = show ? "" : "none";
+ let name = br1 ? br1.previousElementSibling : null;
+ if (name) name.style.display = show ? "" : "none";
+ let info = name ? name.previousElementSibling : null;
+ if (info) info.style.display = show ? "" : "none";
+ };
+
+ toggleVisibility("UPSCALING_PRESET", useFSR);
+ toggleVisibility("FSR_SHARPNESS", useFSR);
+ toggleVisibility("RENDER_RESOLUTION", !useFSR || preset === "Custom");
+}
+
function updateRenderResolution() {
- let resRatio = 1.0;
- if (typeof getCMV === "function" && getCMV("RENDER_RESOLUTION") !== undefined) {
- resRatio = getCMV("RENDER_RESOLUTION");
+ let aaMode = typeof getCMV === "function" ? getCMV("AA_MODE") || "" : "";
+ let resRatio = getRenderResRatio();
+
+ if (aaMode.includes("FSR")) {
+ renderer.setPixelRatio(window.devicePixelRatio); // FSR handle scale
+ } else {
+ renderer.setPixelRatio(window.devicePixelRatio * resRatio);
}
- renderer.setPixelRatio(window.devicePixelRatio * resRatio);
}
// --- Anti-Aliasing Post-Processing Pipeline ---
@@ -133,6 +190,10 @@ let ppRenderTarget = null;
let ppScene = null;
let ppCamera = null;
let ppQuad = null;
+let fxaaRenderTarget = null;
+let easuRenderTarget = null;
+let easuQuad = null;
+let rcasQuad = null;
const _fxaaVertexShader = [
'varying vec2 vUv;',
@@ -145,6 +206,7 @@ const _fxaaVertexShader = [
const _fxaaFragmentShader = [
'uniform sampler2D tDiffuse;',
'uniform vec2 resolution;',
+ 'uniform bool convertToSRGB;',
'varying vec2 vUv;',
'',
'#define FXAA_REDUCE_MIN (1.0 / 128.0)',
@@ -196,7 +258,11 @@ const _fxaaFragmentShader = [
' } else {',
' result = rgbB.xyz;',
' }',
- ' gl_FragColor = vec4(linearToSRGB(result), texColor.a);',
+ ' if (convertToSRGB) {',
+ ' gl_FragColor = vec4(linearToSRGB(result), texColor.a);',
+ ' } else {',
+ ' gl_FragColor = vec4(result, texColor.a);',
+ ' }',
'}'
].join('\n');
@@ -212,59 +278,123 @@ const _passthroughFragmentShader = [
'}'
].join('\n');
-function setupPostProcessing(useFXAA, useMSAA) {
- // Dispose old resources
+function setupPostProcessing(useFXAA, useMSAA, useFSR) {
if (ppRenderTarget) { ppRenderTarget.dispose(); ppRenderTarget = null; }
- if (ppQuad) {
- ppQuad.geometry.dispose();
- ppQuad.material.dispose();
- ppQuad = null;
- }
+ if (fxaaRenderTarget) { fxaaRenderTarget.dispose(); fxaaRenderTarget = null; }
+ if (easuRenderTarget) { easuRenderTarget.dispose(); easuRenderTarget = null; }
+ if (ppQuad) { ppQuad.geometry.dispose(); ppQuad.material.dispose(); ppQuad = null; }
+ if (easuQuad) { easuQuad.geometry.dispose(); easuQuad = null; }
+ if (rcasQuad) { rcasQuad.geometry.dispose(); rcasQuad = null; }
ppScene = null;
ppCamera = null;
- if (!useFXAA && !useMSAA) return;
+ let resRatio = getRenderResRatio();
+ if (!useFXAA && !useMSAA && !useFSR && resRatio === 1.0) return;
let size = renderer.getSize(new THREE.Vector2());
- let pixelRatio = renderer.getPixelRatio();
- let w = Math.floor(size.x * pixelRatio);
- let h = Math.floor(size.y * pixelRatio);
+ let displayW = Math.floor(size.x * window.devicePixelRatio);
+ let displayH = Math.floor(size.y * window.devicePixelRatio);
+ let renderW = displayW;
+ let renderH = displayH;
+
+ if (useFSR) {
+ renderW = Math.floor(displayW * resRatio);
+ renderH = Math.floor(displayH * resRatio);
+ } else {
+ let pixelRatio = renderer.getPixelRatio();
+ renderW = Math.floor(size.x * pixelRatio);
+ renderH = Math.floor(size.y * pixelRatio);
+ }
let targetOptions = {};
if (useMSAA) targetOptions.samples = 4;
- ppRenderTarget = new THREE.WebGLRenderTarget(w, h, targetOptions);
-
- let fragShader = useFXAA ? _fxaaFragmentShader : _passthroughFragmentShader;
- let uniforms = { tDiffuse: { value: ppRenderTarget.texture } };
- if (useFXAA) {
- uniforms.resolution = { value: new THREE.Vector2(w, h) };
- }
-
- let material = new THREE.ShaderMaterial({
- uniforms: uniforms,
- vertexShader: _fxaaVertexShader,
- fragmentShader: fragShader,
- depthTest: false,
- depthWrite: false,
- blending: THREE.NoBlending,
- toneMapped: false
- });
-
+ ppRenderTarget = new THREE.WebGLRenderTarget(renderW, renderH, targetOptions);
ppCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
ppScene = new THREE.Scene();
- ppQuad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material);
- ppScene.add(ppQuad);
+
+ if (useFSR && typeof setupFSR === 'function') {
+ easuRenderTarget = new THREE.WebGLRenderTarget(displayW, displayH);
+ if (useFXAA) {
+ fxaaRenderTarget = new THREE.WebGLRenderTarget(renderW, renderH);
+ let fxaaMat = new THREE.ShaderMaterial({
+ uniforms: {
+ tDiffuse: { value: ppRenderTarget.texture },
+ resolution: { value: new THREE.Vector2(renderW, renderH) },
+ convertToSRGB: { value: false } // Output linear for FSR
+ },
+ vertexShader: _fxaaVertexShader,
+ fragmentShader: _fxaaFragmentShader,
+ depthTest: false, depthWrite: false, blending: THREE.NoBlending, toneMapped: false
+ });
+ ppQuad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), fxaaMat);
+ }
+ setupFSR(displayW, displayH, renderW, renderH);
+ fsrEASUMaterial.uniforms.tDiffuse.value = useFXAA ? fxaaRenderTarget.texture : ppRenderTarget.texture;
+ fsrRCASMaterial.uniforms.tDiffuse.value = easuRenderTarget.texture;
+ easuQuad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), fsrEASUMaterial);
+ rcasQuad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), fsrRCASMaterial);
+ } else {
+ let fragShader = useFXAA ? _fxaaFragmentShader : _passthroughFragmentShader;
+ let uniforms = { tDiffuse: { value: ppRenderTarget.texture } };
+ if (useFXAA) {
+ uniforms.resolution = { value: new THREE.Vector2(renderW, renderH) };
+ uniforms.convertToSRGB = { value: true }; // Final pass, output sRGB
+ }
+ let material = new THREE.ShaderMaterial({
+ uniforms: uniforms, vertexShader: _fxaaVertexShader, fragmentShader: fragShader,
+ depthTest: false, depthWrite: false, blending: THREE.NoBlending, toneMapped: false
+ });
+ ppQuad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material);
+ ppScene.add(ppQuad);
+ }
}
function updatePostProcessingSize() {
if (!ppRenderTarget) return;
+
let size = renderer.getSize(new THREE.Vector2());
- let pixelRatio = renderer.getPixelRatio();
- let w = Math.floor(size.x * pixelRatio);
- let h = Math.floor(size.y * pixelRatio);
- ppRenderTarget.setSize(w, h);
- if (ppQuad && ppQuad.material.uniforms.resolution) {
- ppQuad.material.uniforms.resolution.value.set(w, h);
+ let displayW = Math.floor(size.x * window.devicePixelRatio);
+ let displayH = Math.floor(size.y * window.devicePixelRatio);
+
+ let aaMode = typeof getCMV === "function" ? getCMV("AA_MODE") || "" : "";
+ let useFSR = aaMode.includes("FSR");
+ let resRatio = getRenderResRatio();
+
+ let renderW = displayW;
+ let renderH = displayH;
+
+ if (useFSR) {
+ renderW = Math.floor(displayW * resRatio);
+ renderH = Math.floor(displayH * resRatio);
+ } else {
+ let pixelRatio = renderer.getPixelRatio();
+ renderW = Math.floor(size.x * pixelRatio);
+ renderH = Math.floor(size.y * pixelRatio);
+ }
+
+ // Always resize base target to render resolution
+ ppRenderTarget.setSize(renderW, renderH);
+
+ if (useFSR) {
+ if (easuRenderTarget) easuRenderTarget.setSize(displayW, displayH);
+ if (fxaaRenderTarget) fxaaRenderTarget.setSize(renderW, renderH);
+
+ // Update FSR uniform resolutions
+ if (typeof fsrEASUMaterial !== 'undefined' && fsrEASUMaterial) {
+ fsrEASUMaterial.uniforms.resolution.value.set(displayW, displayH);
+ fsrEASUMaterial.uniforms.renderResolution.value.set(renderW, renderH);
+ }
+ if (typeof fsrRCASMaterial !== 'undefined' && fsrRCASMaterial) {
+ fsrRCASMaterial.uniforms.resolution.value.set(displayW, displayH);
+ }
+ if (ppQuad && ppQuad.material.uniforms.resolution) {
+ ppQuad.material.uniforms.resolution.value.set(renderW, renderH);
+ }
+ } else {
+ // Standard pass FXAA resolution update
+ if (ppQuad && ppQuad.material.uniforms.resolution) {
+ ppQuad.material.uniforms.resolution.value.set(renderW, renderH);
+ }
}
}
@@ -275,7 +405,8 @@ function applyAAMode() {
}
let useMSAA = aaMode.indexOf('MSAA') !== -1;
let useFXAA = aaMode.indexOf('FXAA') !== -1;
- setupPostProcessing(useFXAA, useMSAA);
+ let useFSR = aaMode.indexOf('FSR') !== -1;
+ setupPostProcessing(useFXAA, useMSAA, useFSR);
}
function recreateRenderer() {
@@ -750,8 +881,9 @@ function createLayout() {
setTrackingModeSelect(itemselect.value);
} else if (configitem['key'] == "UI_THEME") {
updateTheme();
- } else if (configitem['key'] === 'AA_MODE') {
- applyAAMode();
+ } else if (configitem['key'] === 'AA_MODE' || configitem['key'] === 'UPSCALING_PRESET') {
+ updateRenderingUI();
+ recreateRenderer();
}
};
confgroup.appendChild(itemselect);
@@ -759,7 +891,7 @@ function createLayout() {
item.setAttribute("type", "range");
item.setAttribute("min", 0);
item.setAttribute("max", 1000);
- let setrange = configitem['range'][1] - configitem['range'][0];
+ let setrange = configitem['range'][1] - configitem['range'][0]; // ADDED BACK
let setvalue = (getCMV(configitem['key']) - configitem['range'][0]) * 1000 / setrange;
item.setAttribute("value", setvalue);
item.onchange = function () {
@@ -779,9 +911,9 @@ function createLayout() {
itemval.value = configitem['range'][1];
}
let newvalue = (itemval.value - configitem['range'][0]) * 1000 / setrange;
- item.setAttribute("value", newvalue);
+ item.value = newvalue;
setCMV(configitem['key'], itemval.value);
- if (configitem['key'] === 'RENDER_RESOLUTION') {
+ if (configitem['key'] === 'RENDER_RESOLUTION' || configitem['key'] === 'FSR_SHARPNESS') {
updateRenderResolution();
updatePostProcessingSize();
}
@@ -874,6 +1006,7 @@ function createLayout() {
about.appendChild(document.createElement("br"));
}
+ updateRenderingUI();
console.log("gui layout initialized");
}
@@ -1162,16 +1295,20 @@ function raiseAlert(vistate, mlstate) {
let alertbox = document.getElementById("alertbox");
alertbox.style.display = "block";
let alerttext = document.getElementById("alerttext");
+
if (vistate == 3) {
alerttext.innerHTML = getL("ALERT: Full Screen / Wrong Tab
Browser will stop rendering when other program enters full screen!");
} else if (mlstate == 3) {
alerttext.innerHTML = getL("ALERT: Error
ML loop stop running, might need to restart to validate.");
} else if (mlstate == 2 || vistate == 2) {
alerttext.innerHTML = getL("ALERT: Hardware Acceleration
ML loop is running extremely slow, check if hardware acceleration is opened.");
- } else if (mlstate == 1 && getCMV("HAND_TRACKING")) {
+ } else if (mlstate == 1 && getCMV("TRACKING_MODE") !== "Face-Only") {
alerttext.innerHTML = getL("ALERT: Ultra Fast
ML loop is running slowly, improve performance by using FACE-ONLY mode.");
} else if (vistate == 1) {
alerttext.innerHTML = getL("ALERT: Slow
Feel free to contact developer for more information.");
+ } else {
+ // Fallback just in case
+ alerttext.innerHTML = "Performance Warning: Tracking FPS dropped.";
}
}
}
@@ -1188,15 +1325,40 @@ function drawScene() {
setCMV('SCENE_FLIP', getCMV('CAMERA_FLIP'));
scene.applyMatrix4(new THREE.Matrix4().makeScale(-1, 1, 1));
}
- if (ppRenderTarget && ppQuad) {
- // Render scene to LINEAR render target
+
+ let aaMode = typeof getCMV === "function" ? getCMV("AA_MODE") || "" : "";
+ let useFSR = aaMode.includes("FSR");
+
+ if (ppRenderTarget) {
renderer.outputEncoding = THREE.LinearEncoding;
renderer.setRenderTarget(ppRenderTarget);
renderer.render(scene, camera);
- // Render quad to screen (shader applies linearToSRGB)
- renderer.setRenderTarget(null);
- renderer.outputEncoding = THREE.sRGBEncoding;
- renderer.render(ppScene, ppCamera);
+
+ if (useFSR && typeof setupFSR === 'function') {
+ if (ppQuad && fxaaRenderTarget) {
+ renderer.setRenderTarget(fxaaRenderTarget);
+ ppScene.add(ppQuad);
+ renderer.render(ppScene, ppCamera);
+ ppScene.remove(ppQuad);
+ }
+ renderer.setRenderTarget(easuRenderTarget);
+ ppScene.add(easuQuad);
+ renderer.render(ppScene, ppCamera);
+ ppScene.remove(easuQuad);
+
+ renderer.setRenderTarget(null);
+ renderer.outputEncoding = THREE.sRGBEncoding;
+ if (typeof fsrRCASMaterial !== 'undefined' && fsrRCASMaterial) {
+ fsrRCASMaterial.uniforms.sharpness.value = getCMV("FSR_SHARPNESS") !== undefined ? getCMV("FSR_SHARPNESS") : 0.2;
+ }
+ ppScene.add(rcasQuad);
+ renderer.render(ppScene, ppCamera);
+ ppScene.remove(rcasQuad);
+ } else {
+ renderer.setRenderTarget(null);
+ renderer.outputEncoding = THREE.sRGBEncoding;
+ renderer.render(ppScene, ppCamera);
+ }
} else {
renderer.render(scene, camera);
}