From f7aa7441835bf1bb5796e26106d65776e044c1c9 Mon Sep 17 00:00:00 2001 From: Lila Date: Tue, 8 Jul 2025 20:25:43 +0100 Subject: [PATCH 01/16] change texture shader location --- renderer.py | 39 ++++++------- shader/main3d.frag.glsl | 122 ---------------------------------------- shader/textures.glsl | 122 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 141 deletions(-) create mode 100644 shader/textures.glsl diff --git a/renderer.py b/renderer.py index a0c307f..0179b6a 100644 --- a/renderer.py +++ b/renderer.py @@ -1,3 +1,4 @@ +from io import StringIO import math import pathlib import time @@ -97,24 +98,24 @@ def init_shader(self, scene: bpy.types.Scene): print("Compiling shader") shaderPath = (pathlib.Path(__file__).parent / "shader").resolve() - shaderVert = "" - shaderFrag = "" - - with open(shaderPath / "utils.glsl", "r", encoding="utf-8") as f: - shaderUtils = f.read() - shaderVert += shaderUtils - shaderFrag += shaderUtils - - with open(shaderPath / "defines.glsl", "r", encoding="utf-8") as f: - shaderDef = f.read() - shaderVert += shaderDef - shaderFrag += shaderDef - - with open(shaderPath / "main3d.vert.glsl", "r", encoding="utf-8") as f: - shaderVert += f.read() + shaderVert = StringIO() + shaderFrag = StringIO() + + general_shaders = ("utils.glsl", "defines.glsl") + vertex_shaders = ("main3d.vert.glsl",) + frag_shaders = ( + "textures.glsl", + "main3d.frag.glsl", + ) - with open(shaderPath / "main3d.frag.glsl", "r", encoding="utf-8") as f: - shaderFrag += f.read() + for shader in general_shaders + vertex_shaders: + with open(shaderPath / shader, "r", encoding="utf-8") as f: + shaderVert.write(f.read()) + shaderVert.write("\n") + for shader in general_shaders + frag_shaders: + with open(shaderPath / shader, "r", encoding="utf-8") as f: + shaderFrag.write(f.read()) + shaderFrag.write("\n") shader_info = gpu.types.GPUShaderCreateInfo() @@ -158,8 +159,8 @@ def init_shader(self, scene: bpy.types.Scene): else: shader_info.fragment_out(0, "VEC4", "FragColor") - shader_info.vertex_source(shaderVert) - shader_info.fragment_source(shaderFrag) + shader_info.vertex_source(shaderVert.getvalue()) + shader_info.fragment_source(shaderFrag.getvalue()) self.shader = gpu.shader.create_from_info(shader_info) self.shader_fallback = gpu.shader.from_builtin( diff --git a/shader/main3d.frag.glsl b/shader/main3d.frag.glsl index 9dc2bc1..871477e 100644 --- a/shader/main3d.frag.glsl +++ b/shader/main3d.frag.glsl @@ -3,131 +3,9 @@ #extension GL_ARB_fragment_shader_interlock : enable layout(pixel_interlock_unordered) in; #endif -#ifdef GL_ARB_derivative_control - #extension GL_ARB_derivative_control : enable -#endif #define DECAL_DEPTH_DELTA 100 -vec4 quantize3Bit(in vec4 color) { - return vec4(round(color.rgb * 8.0) / 8.0, step(0.5, color.a)); -} - -vec4 quantize4Bit(in vec4 color) { - return round(color * 16.0) / 16.0; // (16 seems more accurate than 15) -} - -vec4 quantizeTexture(uint flags, vec4 color) { - vec4 colorQuant = flagSelect(flags, TEX_FLAG_4BIT, color, quantize4Bit(color)); - colorQuant = flagSelect(flags, TEX_FLAG_3BIT, colorQuant, quantize3Bit(colorQuant)); - colorQuant.rgb = linearToGamma(colorQuant.rgb); - return flagSelect(flags, TEX_FLAG_MONO, colorQuant.rgba, colorQuant.rrrr); -} - -vec4 sampleSampler(in const sampler2D tex, in const TileConf tileConf, in vec2 uvCoord, in const uint texFilter) { - // https://github.com/rt64/rt64/blob/61aa08f517cd16c1dbee4e097768b08e2a060307/src/shaders/TextureSampler.hlsli#L156-L276 - const ivec2 texSize = textureSize(tex, 0); - - uvCoord.y = texSize.y - uvCoord.y; // invert Y - uvCoord *= tileConf.shift; - -#ifdef SIMULATE_LOW_PRECISION - // Simulates the lower precision of the hardware's coordinate interpolation. - uvCoord = round(uvCoord * LOW_PRECISION) / LOW_PRECISION; -#endif - - uvCoord -= tileConf.low; - - const vec2 isClamp = step(tileConf.mask, vec2(1.0)); - const vec2 isMirror = step(tileConf.high, vec2(0.0)); - const vec2 isForceClamp = step(tileConf.mask, vec2(1.0)); // mask == 0 forces clamping - const vec2 mask = mix(abs(tileConf.mask), vec2(256), isForceClamp); // if mask == 0, we also have to ignore it - const vec2 highMinusLow = abs(tileConf.high) - abs(tileConf.low); - - if (texFilter != G_TF_POINT) { - uvCoord -= 0.5 * tileConf.shift; - const ivec2 texelBaseInt = ivec2(floor(uvCoord)); - const vec4 sample00 = wrappedMirrorSample(tex, texelBaseInt, mask, highMinusLow, isClamp, isMirror, isForceClamp); - const vec4 sample01 = wrappedMirrorSample(tex, texelBaseInt + ivec2(0, 1), mask, highMinusLow, isClamp, isMirror, isForceClamp); - const vec4 sample10 = wrappedMirrorSample(tex, texelBaseInt + ivec2(1, 0), mask, highMinusLow, isClamp, isMirror, isForceClamp); - const vec4 sample11 = wrappedMirrorSample(tex, texelBaseInt + ivec2(1, 1), mask, highMinusLow, isClamp, isMirror, isForceClamp); - const vec2 fracPart = uvCoord - texelBaseInt; -#ifdef USE_LINEAR_FILTER - return quantizeTexture(tileConf.flags, mix(mix(sample00, sample10, fracPart.x), mix(sample01, sample11, fracPart.x), fracPart.y)); -#else - if (texFilter == G_TF_AVERAGE && all(lessThanEqual(vec2(1 / LOW_PRECISION), abs(fracPart - 0.5)))) { - return quantizeTexture(tileConf.flags, (sample00 + sample01 + sample10 + sample11) / 4.0f); - } - else { - // Originally written by ArthurCarvalho - // Sourced from https://www.emutalk.net/threads/emulating-nintendo-64-3-sample-bilinear-filtering-using-shaders.54215/ - vec4 tri0 = mix(sample00, sample10, fracPart.x) + (sample01 - sample00) * fracPart.y; - vec4 tri1 = mix(sample11, sample01, 1.0 - fracPart.x) + (sample10 - sample11) * (1.0 - fracPart.y); - return quantizeTexture(tileConf.flags, mix(tri0, tri1, step(1.0, fracPart.x + fracPart.y))); - } -#endif - } - else { - return quantizeTexture(tileConf.flags, wrappedMirrorSample(tex, ivec2(floor(uvCoord)), mask, highMinusLow, isClamp, isMirror, isForceClamp)); - } -} - -vec4 sampleIndex(in const uint textureIndex, in const vec2 uvCoord, in const uint texFilter) { - TileConf tileConf = material.texConfs[textureIndex]; - switch (textureIndex) { - default: return sampleSampler(tex0, tileConf, uvCoord, texFilter); - case 1: return sampleSampler(tex1, tileConf, uvCoord, texFilter); - case 2: return sampleSampler(tex2, tileConf, uvCoord, texFilter); - case 3: return sampleSampler(tex3, tileConf, uvCoord, texFilter); - case 4: return sampleSampler(tex4, tileConf, uvCoord, texFilter); - case 5: return sampleSampler(tex5, tileConf, uvCoord, texFilter); - case 6: return sampleSampler(tex6, tileConf, uvCoord, texFilter); - case 7: return sampleSampler(tex7, tileConf, uvCoord, texFilter); - } -} - -float computeLOD(inout uint tileIndex0, inout uint tileIndex1) { - // https://github.com/rt64/rt64/blob/0ca92eeb6c2f58ce3581c65f87f7261b8ac0fea0/src/shaders/TextureSampler.hlsli#L18 - if (textLOD() == G_TL_TILE) - return 1.0f; - const uint texDetail = textDetail(); - const bool lodSharpen = texDetail == G_TD_SHARPEN; - const bool lodDetail = texDetail == G_TD_DETAIL; - const bool lodSharpDetail = lodSharpen || lodDetail; - -#ifdef GL_ARB_derivative_control - const vec2 dfd = abs(vec2(dFdxCoarse(inputUV.x), dFdyCoarse(inputUV.y))); -#else - const vec2 dfd = abs(vec2(dFdx(inputUV.x), dFdy(inputUV.y))); -#endif - float maxDst = max(dfd.x, dfd.y); - - if (lodSharpDetail) - maxDst = max(maxDst, material.primLod.y); - - int tileBase = int(floor(log2(maxDst))); - float lodFraction = maxDst / pow(2, max(tileBase, 0)) - 1.0; - - if (lodSharpen && maxDst < 1.0) - lodFraction = maxDst - 1.0; - - if (lodDetail) { - if (lodFraction < 0.0) - lodFraction = maxDst; - tileBase += 1; - } else if (tileBase >= material.mipCount) - lodFraction = 1.0; - - if (lodSharpDetail) - tileBase = max(tileBase, 0); - else - lodFraction = max(lodFraction, 0.0); - - tileIndex0 = clamp(tileBase, 0, material.mipCount); - tileIndex1 = clamp(tileBase + 1, 0, material.mipCount); - return lodFraction; -} - vec3 cc_fetchColor(in int val, in vec4 shade, in vec4 comb, in float lodFraction, in vec4 texData0, in vec4 texData1) { if(val == CC_C_COMB ) return comb.rgb; diff --git a/shader/textures.glsl b/shader/textures.glsl new file mode 100644 index 0000000..7a53144 --- /dev/null +++ b/shader/textures.glsl @@ -0,0 +1,122 @@ +#ifdef GL_ARB_derivative_control + #extension GL_ARB_derivative_control : enable +#endif + +vec4 quantize3Bit(in vec4 color) { + return vec4(round(color.rgb * 8.0) / 8.0, step(0.5, color.a)); +} + +vec4 quantize4Bit(in vec4 color) { + return round(color * 16.0) / 16.0; // (16 seems more accurate than 15) +} + +vec4 quantizeTexture(uint flags, vec4 color) { + vec4 colorQuant = flagSelect(flags, TEX_FLAG_4BIT, color, quantize4Bit(color)); + colorQuant = flagSelect(flags, TEX_FLAG_3BIT, colorQuant, quantize3Bit(colorQuant)); + colorQuant.rgb = linearToGamma(colorQuant.rgb); + return flagSelect(flags, TEX_FLAG_MONO, colorQuant.rgba, colorQuant.rrrr); +} + +vec4 sampleSampler(in const sampler2D tex, in const TileConf tileConf, in vec2 uvCoord, in const uint texFilter) { + // https://github.com/rt64/rt64/blob/61aa08f517cd16c1dbee4e097768b08e2a060307/src/shaders/TextureSampler.hlsli#L156-L276 + const ivec2 texSize = textureSize(tex, 0); + + uvCoord.y = texSize.y - uvCoord.y; // invert Y + uvCoord *= tileConf.shift; + +#ifdef SIMULATE_LOW_PRECISION + // Simulates the lower precision of the hardware's coordinate interpolation. + uvCoord = round(uvCoord * LOW_PRECISION) / LOW_PRECISION; +#endif + + uvCoord -= tileConf.low; + + const vec2 isClamp = step(tileConf.mask, vec2(1.0)); + const vec2 isMirror = step(tileConf.high, vec2(0.0)); + const vec2 isForceClamp = step(tileConf.mask, vec2(1.0)); // mask == 0 forces clamping + const vec2 mask = mix(abs(tileConf.mask), vec2(256), isForceClamp); // if mask == 0, we also have to ignore it + const vec2 highMinusLow = abs(tileConf.high) - abs(tileConf.low); + + if (texFilter != G_TF_POINT) { + uvCoord -= 0.5 * tileConf.shift; + const ivec2 texelBaseInt = ivec2(floor(uvCoord)); + const vec4 sample00 = wrappedMirrorSample(tex, texelBaseInt, mask, highMinusLow, isClamp, isMirror, isForceClamp); + const vec4 sample01 = wrappedMirrorSample(tex, texelBaseInt + ivec2(0, 1), mask, highMinusLow, isClamp, isMirror, isForceClamp); + const vec4 sample10 = wrappedMirrorSample(tex, texelBaseInt + ivec2(1, 0), mask, highMinusLow, isClamp, isMirror, isForceClamp); + const vec4 sample11 = wrappedMirrorSample(tex, texelBaseInt + ivec2(1, 1), mask, highMinusLow, isClamp, isMirror, isForceClamp); + const vec2 fracPart = uvCoord - texelBaseInt; +#ifdef USE_LINEAR_FILTER + return quantizeTexture(tileConf.flags, mix(mix(sample00, sample10, fracPart.x), mix(sample01, sample11, fracPart.x), fracPart.y)); +#else + if (texFilter == G_TF_AVERAGE && all(lessThanEqual(vec2(1 / LOW_PRECISION), abs(fracPart - 0.5)))) { + return quantizeTexture(tileConf.flags, (sample00 + sample01 + sample10 + sample11) / 4.0f); + } + else { + // Originally written by ArthurCarvalho + // Sourced from https://www.emutalk.net/threads/emulating-nintendo-64-3-sample-bilinear-filtering-using-shaders.54215/ + vec4 tri0 = mix(sample00, sample10, fracPart.x) + (sample01 - sample00) * fracPart.y; + vec4 tri1 = mix(sample11, sample01, 1.0 - fracPart.x) + (sample10 - sample11) * (1.0 - fracPart.y); + return quantizeTexture(tileConf.flags, mix(tri0, tri1, step(1.0, fracPart.x + fracPart.y))); + } +#endif + } + else { + return quantizeTexture(tileConf.flags, wrappedMirrorSample(tex, ivec2(floor(uvCoord)), mask, highMinusLow, isClamp, isMirror, isForceClamp)); + } +} + +vec4 sampleIndex(in const uint textureIndex, in const vec2 uvCoord, in const uint texFilter) { + TileConf tileConf = material.texConfs[textureIndex]; + switch (textureIndex) { + default: return sampleSampler(tex0, tileConf, uvCoord, texFilter); + case 1: return sampleSampler(tex1, tileConf, uvCoord, texFilter); + case 2: return sampleSampler(tex2, tileConf, uvCoord, texFilter); + case 3: return sampleSampler(tex3, tileConf, uvCoord, texFilter); + case 4: return sampleSampler(tex4, tileConf, uvCoord, texFilter); + case 5: return sampleSampler(tex5, tileConf, uvCoord, texFilter); + case 6: return sampleSampler(tex6, tileConf, uvCoord, texFilter); + case 7: return sampleSampler(tex7, tileConf, uvCoord, texFilter); + } +} + +float computeLOD(inout uint tileIndex0, inout uint tileIndex1) { + // https://github.com/rt64/rt64/blob/0ca92eeb6c2f58ce3581c65f87f7261b8ac0fea0/src/shaders/TextureSampler.hlsli#L18 + if (textLOD() == G_TL_TILE) + return 1.0f; + const uint texDetail = textDetail(); + const bool lodSharpen = texDetail == G_TD_SHARPEN; + const bool lodDetail = texDetail == G_TD_DETAIL; + const bool lodSharpDetail = lodSharpen || lodDetail; + +#ifdef GL_ARB_derivative_control + const vec2 dfd = abs(vec2(dFdxCoarse(inputUV.x), dFdyCoarse(inputUV.y))); +#else + const vec2 dfd = abs(vec2(dFdx(inputUV.x), dFdy(inputUV.y))); +#endif + float maxDst = max(dfd.x, dfd.y); + + if (lodSharpDetail) + maxDst = max(maxDst, material.primLod.y); + + int tileBase = int(floor(log2(maxDst))); + float lodFraction = maxDst / pow(2, max(tileBase, 0)) - 1.0; + + if (lodSharpen && maxDst < 1.0) + lodFraction = maxDst - 1.0; + + if (lodDetail) { + if (lodFraction < 0.0) + lodFraction = maxDst; + tileBase += 1; + } else if (tileBase >= material.mipCount) + lodFraction = 1.0; + + if (lodSharpDetail) + tileBase = max(tileBase, 0); + else + lodFraction = max(lodFraction, 0.0); + + tileIndex0 = clamp(tileBase, 0, material.mipCount); + tileIndex1 = clamp(tileBase + 1, 0, material.mipCount); + return lodFraction; +} \ No newline at end of file From 471bed8e456cdd6a82dfb8eac73beded269c9e6e Mon Sep 17 00:00:00 2001 From: Lila Date: Wed, 9 Jul 2025 15:29:18 +0100 Subject: [PATCH 02/16] quantize lod --- material/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/material/parser.py b/material/parser.py index d7b1bf9..ecf15f8 100644 --- a/material/parser.py +++ b/material/parser.py @@ -394,7 +394,7 @@ def f64_material_parse(f3d_mat: "F3DMaterialProperty", always_set: bool, set_lig state.cc = get_cc_settings(f3d_mat) if always_set or (f3d_mat.set_prim and cc_uses["Primitive"]): state.prim_color = quantize_srgb(f3d_mat.prim_color) - state.prim_lod = (f3d_mat.prim_lod_frac, f3d_mat.prim_lod_min) + state.prim_lod = quantize_tuple((f3d_mat.prim_lod_frac, f3d_mat.prim_lod_min), 8) if always_set or (f3d_mat.set_env and cc_uses["Environment"]): state.env_color = quantize_srgb(f3d_mat.env_color) if always_set or (f3d_mat.set_key and cc_uses["Key"]): # extra 0 for alignment From 3e4505c530d94ac8cf67067985b5eeead7ce1685 Mon Sep 17 00:00:00 2001 From: Lila Date: Wed, 9 Jul 2025 16:22:38 +0100 Subject: [PATCH 03/16] first iteration of the parallel rdp code (suprisingly less branch-happy then rt64) --- shader/main3d.frag.glsl | 11 ++++- shader/textures.glsl | 105 +++++++++++++++++++++++++--------------- 2 files changed, 76 insertions(+), 40 deletions(-) diff --git a/shader/main3d.frag.glsl b/shader/main3d.frag.glsl index 871477e..02ee7e8 100644 --- a/shader/main3d.frag.glsl +++ b/shader/main3d.frag.glsl @@ -144,9 +144,18 @@ void main() vec4 ccShade = geoModeSelect(G_SHADE_SMOOTH, cc_shade_flat, cc_shade); +#ifdef GL_ARB_derivative_control + const vec2 dx = abs(vec2(dFdxCoarse(inputUV.x), dFdyCoarse(inputUV.x))); + const vec2 dy = abs(vec2(dFdxCoarse(inputUV.y), dFdyCoarse(inputUV.y))); +#else + const vec2 dx = abs(vec2(dFdx(inputUV.x), dFdy(inputUV.x))); + const vec2 dy = abs(vec2(dFdx(inputUV.y), dFdy(inputUV.y))); +#endif + uint tex0Index = 0; uint tex1Index = 1; - const float lodFraction = computeLOD(tex0Index, tex1Index); + float lodFraction = 0.0; + computeLOD(tex0Index, tex1Index, material.primLod.y, dx, dy, false, lodFraction); vec4 texData0 = sampleIndex(tex0Index, inputUV, texFilter); vec4 texData1 = sampleIndex(tex1Index, inputUV, texFilter); diff --git a/shader/textures.glsl b/shader/textures.glsl index 7a53144..b90f816 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -79,44 +79,71 @@ vec4 sampleIndex(in const uint textureIndex, in const vec2 uvCoord, in const uin } } -float computeLOD(inout uint tileIndex0, inout uint tileIndex1) { - // https://github.com/rt64/rt64/blob/0ca92eeb6c2f58ce3581c65f87f7261b8ac0fea0/src/shaders/TextureSampler.hlsli#L18 - if (textLOD() == G_TL_TILE) - return 1.0f; - const uint texDetail = textDetail(); - const bool lodSharpen = texDetail == G_TD_SHARPEN; - const bool lodDetail = texDetail == G_TD_DETAIL; - const bool lodSharpDetail = lodSharpen || lodDetail; - -#ifdef GL_ARB_derivative_control - const vec2 dfd = abs(vec2(dFdxCoarse(inputUV.x), dFdyCoarse(inputUV.y))); -#else - const vec2 dfd = abs(vec2(dFdx(inputUV.x), dFdy(inputUV.y))); +// #define EMULATE_PERSPECTIVE_OVERFLOW 1 + +void computeLOD( + inout uint tileIndex0, + inout uint tileIndex1, + float minLod, + vec2 dx, + vec2 dy, + bool perspective_overflow, + out float lodFrac +) { + const bool textLOD = bool(textLOD()); + const uint textDetail = textDetail(); + const bool sharpen = textDetail == G_TD_SHARPEN; + const bool detail = textDetail == G_TD_DETAIL; + + bool magnify = false; + bool distant = false; + + uint tile_offset = 0; + +#ifdef EMULATE_PERSPECTIVE_OVERFLOW // this should be possible from what I've read in parallel-rdp, can always be removed + if (perspective_overflow) { + distant = true; + lodFrac = 1.0; + } else { +#endif + vec2 dfd = max(dx, dy); + float max_d = max(dfd.x, dfd.y); + // TODO: should this value be scaled by clipping planes? + if (max_d >= 16384.0) { // max delta very large + distant = true; + lodFrac = 1.0; + } else if (max_d < 1.0) { // magnification + magnify = true; + distant = material.mipCount == 0; + const float detailFrac = max(minLod, max_d) - float(sharpen); + lodFrac = bool(textDetail) ? detailFrac : float(distant); + } else { + uint mip_base = uint(floor(log2(max_d))); + distant = mip_base >= material.mipCount; + + if (distant && textDetail == G_TD_CLAMP) { + lodFrac = 1.0; + } else { + lodFrac = max_d / pow(2, max(mip_base, 0)) - 1.0; + lodFrac = max(lodFrac, material.primLod.y); + tile_offset = mip_base; + } + } +#ifdef EMULATE_PERSPECTIVE_OVERFLOW + } #endif - float maxDst = max(dfd.x, dfd.y); - - if (lodSharpDetail) - maxDst = max(maxDst, material.primLod.y); - - int tileBase = int(floor(log2(maxDst))); - float lodFraction = maxDst / pow(2, max(tileBase, 0)) - 1.0; - - if (lodSharpen && maxDst < 1.0) - lodFraction = maxDst - 1.0; - - if (lodDetail) { - if (lodFraction < 0.0) - lodFraction = maxDst; - tileBase += 1; - } else if (tileBase >= material.mipCount) - lodFraction = 1.0; - - if (lodSharpDetail) - tileBase = max(tileBase, 0); - else - lodFraction = max(lodFraction, 0.0); - tileIndex0 = clamp(tileBase, 0, material.mipCount); - tileIndex1 = clamp(tileBase + 1, 0, material.mipCount); - return lodFraction; -} \ No newline at end of file + if (textLOD) { + tile_offset = distant ? material.mipCount : tile_offset; + + if (detail) { + tileIndex1 = (tileIndex0 + tile_offset + (int(!(distant || magnify)) + 1)) & 7; + tileIndex0 = (tileIndex0 + tile_offset + int(!magnify)) & 7; + } else { + tileIndex0 = (tileIndex0 + tile_offset) & 7; + tileIndex1 = tileIndex0; + if (!distant && (sharpen || !magnify)) + tileIndex1 = (tileIndex1 + 1) & 7; // Use next mip level + } + } +} From 9485f9738ce7f35ae89cbdd2de41f923d61bd859 Mon Sep 17 00:00:00 2001 From: Lila Date: Wed, 9 Jul 2025 16:26:09 +0100 Subject: [PATCH 04/16] fix naming --- shader/textures.glsl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/shader/textures.glsl b/shader/textures.glsl index b90f816..df76b9b 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -87,7 +87,7 @@ void computeLOD( float minLod, vec2 dx, vec2 dy, - bool perspective_overflow, + bool perspectiveOverflow, out float lodFrac ) { const bool textLOD = bool(textLOD()); @@ -98,35 +98,35 @@ void computeLOD( bool magnify = false; bool distant = false; - uint tile_offset = 0; + uint tileOffset = 0; #ifdef EMULATE_PERSPECTIVE_OVERFLOW // this should be possible from what I've read in parallel-rdp, can always be removed - if (perspective_overflow) { + if (perspectiveOverflow) { distant = true; lodFrac = 1.0; } else { #endif vec2 dfd = max(dx, dy); - float max_d = max(dfd.x, dfd.y); + float maxDist = max(dfd.x, dfd.y); // TODO: should this value be scaled by clipping planes? - if (max_d >= 16384.0) { // max delta very large + if (maxDist >= 16384.0) { // max delta very large distant = true; lodFrac = 1.0; - } else if (max_d < 1.0) { // magnification + } else if (maxDist < 1.0) { // magnification magnify = true; distant = material.mipCount == 0; - const float detailFrac = max(minLod, max_d) - float(sharpen); + const float detailFrac = max(minLod, maxDist) - float(sharpen); lodFrac = bool(textDetail) ? detailFrac : float(distant); } else { - uint mip_base = uint(floor(log2(max_d))); + uint mip_base = uint(floor(log2(maxDist))); distant = mip_base >= material.mipCount; if (distant && textDetail == G_TD_CLAMP) { lodFrac = 1.0; } else { - lodFrac = max_d / pow(2, max(mip_base, 0)) - 1.0; + lodFrac = maxDist / pow(2, max(mip_base, 0)) - 1.0; lodFrac = max(lodFrac, material.primLod.y); - tile_offset = mip_base; + tileOffset = mip_base; } } #ifdef EMULATE_PERSPECTIVE_OVERFLOW @@ -134,13 +134,13 @@ void computeLOD( #endif if (textLOD) { - tile_offset = distant ? material.mipCount : tile_offset; + tileOffset = distant ? material.mipCount : tileOffset; if (detail) { - tileIndex1 = (tileIndex0 + tile_offset + (int(!(distant || magnify)) + 1)) & 7; - tileIndex0 = (tileIndex0 + tile_offset + int(!magnify)) & 7; + tileIndex1 = (tileIndex0 + tileOffset + (int(!(distant || magnify)) + 1)) & 7; + tileIndex0 = (tileIndex0 + tileOffset + int(!magnify)) & 7; } else { - tileIndex0 = (tileIndex0 + tile_offset) & 7; + tileIndex0 = (tileIndex0 + tileOffset) & 7; tileIndex1 = tileIndex0; if (!distant && (sharpen || !magnify)) tileIndex1 = (tileIndex1 + 1) & 7; // Use next mip level From 4c7e54ebf45080fa6d2fa51fd4ff783a5a3e79a4 Mon Sep 17 00:00:00 2001 From: Lila Date: Wed, 9 Jul 2025 17:10:33 +0100 Subject: [PATCH 05/16] first round (working) --- shader/textures.glsl | 56 +++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/shader/textures.glsl b/shader/textures.glsl index df76b9b..0df2c2f 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -79,15 +79,13 @@ vec4 sampleIndex(in const uint textureIndex, in const vec2 uvCoord, in const uin } } -// #define EMULATE_PERSPECTIVE_OVERFLOW 1 - void computeLOD( inout uint tileIndex0, inout uint tileIndex1, float minLod, vec2 dx, vec2 dy, - bool perspectiveOverflow, + bool perspectiveOverflow, // this should be possible from what I've read in parallel-rdp, can always be removed out float lodFrac ) { const bool textLOD = bool(textLOD()); @@ -95,43 +93,27 @@ void computeLOD( const bool sharpen = textDetail == G_TD_SHARPEN; const bool detail = textDetail == G_TD_DETAIL; - bool magnify = false; - bool distant = false; - uint tileOffset = 0; -#ifdef EMULATE_PERSPECTIVE_OVERFLOW // this should be possible from what I've read in parallel-rdp, can always be removed - if (perspectiveOverflow) { - distant = true; - lodFrac = 1.0; - } else { -#endif - vec2 dfd = max(dx, dy); - float maxDist = max(dfd.x, dfd.y); - // TODO: should this value be scaled by clipping planes? - if (maxDist >= 16384.0) { // max delta very large - distant = true; - lodFrac = 1.0; - } else if (maxDist < 1.0) { // magnification - magnify = true; - distant = material.mipCount == 0; - const float detailFrac = max(minLod, maxDist) - float(sharpen); - lodFrac = bool(textDetail) ? detailFrac : float(distant); - } else { - uint mip_base = uint(floor(log2(maxDist))); - distant = mip_base >= material.mipCount; - - if (distant && textDetail == G_TD_CLAMP) { - lodFrac = 1.0; - } else { - lodFrac = maxDist / pow(2, max(mip_base, 0)) - 1.0; - lodFrac = max(lodFrac, material.primLod.y); - tileOffset = mip_base; - } - } -#ifdef EMULATE_PERSPECTIVE_OVERFLOW + const vec2 dfd = max(dx, dy); + const float maxDist = max(dfd.x, dfd.y); + + const uint mip_base = uint(floor(log2(maxDist))); + // TODO: should this value be scaled by clipping planes? + const bool distant0 = perspectiveOverflow || maxDist >= 16384.0; + const bool aboveCount = mip_base >= material.mipCount; + lodFrac = float(distant0 || (aboveCount && textDetail == G_TD_CLAMP)); + const bool distant = distant0 || aboveCount; + const bool magnify = maxDist < 1.0; + + if (!distant0 && maxDist < 1.0) { // magnification + const float detailFrac = max(minLod, maxDist) - float(sharpen); + lodFrac = mix(float(distant), detailFrac, float(textDetail != 0)); + } else if (!distant || textDetail != G_TD_CLAMP) { + lodFrac = maxDist / pow(2, max(mip_base, 0)) - 1.0; + lodFrac = max(lodFrac, material.primLod.y); + tileOffset = mip_base; } -#endif if (textLOD) { tileOffset = distant ? material.mipCount : tileOffset; From 8aab7fdf4badbf812f0cbc99dcdcff26f3150131 Mon Sep 17 00:00:00 2001 From: Lila Date: Wed, 9 Jul 2025 17:12:16 +0100 Subject: [PATCH 06/16] clamp const bool --- shader/textures.glsl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shader/textures.glsl b/shader/textures.glsl index 0df2c2f..71ca040 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -92,6 +92,7 @@ void computeLOD( const uint textDetail = textDetail(); const bool sharpen = textDetail == G_TD_SHARPEN; const bool detail = textDetail == G_TD_DETAIL; + const bool clam = textDetail == G_TD_CLAMP; uint tileOffset = 0; @@ -102,14 +103,14 @@ void computeLOD( // TODO: should this value be scaled by clipping planes? const bool distant0 = perspectiveOverflow || maxDist >= 16384.0; const bool aboveCount = mip_base >= material.mipCount; - lodFrac = float(distant0 || (aboveCount && textDetail == G_TD_CLAMP)); + lodFrac = float(distant0 || (aboveCount && clam)); const bool distant = distant0 || aboveCount; const bool magnify = maxDist < 1.0; if (!distant0 && maxDist < 1.0) { // magnification const float detailFrac = max(minLod, maxDist) - float(sharpen); - lodFrac = mix(float(distant), detailFrac, float(textDetail != 0)); - } else if (!distant || textDetail != G_TD_CLAMP) { + lodFrac = mix(float(distant), detailFrac, float(!clam)); + } else if (!distant || !clam) { lodFrac = maxDist / pow(2, max(mip_base, 0)) - 1.0; lodFrac = max(lodFrac, material.primLod.y); tileOffset = mip_base; From 3db95721013ecc76f6be167cabfd955996f80ebc Mon Sep 17 00:00:00 2001 From: Lila Date: Wed, 9 Jul 2025 17:28:54 +0100 Subject: [PATCH 07/16] bye bye more branches --- shader/textures.glsl | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/shader/textures.glsl b/shader/textures.glsl index 71ca040..ce6b800 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -94,39 +94,37 @@ void computeLOD( const bool detail = textDetail == G_TD_DETAIL; const bool clam = textDetail == G_TD_CLAMP; - uint tileOffset = 0; - const vec2 dfd = max(dx, dy); const float maxDist = max(dfd.x, dfd.y); const uint mip_base = uint(floor(log2(maxDist))); // TODO: should this value be scaled by clipping planes? - const bool distant0 = perspectiveOverflow || maxDist >= 16384.0; + const bool distant = perspectiveOverflow || maxDist >= 16384.0; const bool aboveCount = mip_base >= material.mipCount; - lodFrac = float(distant0 || (aboveCount && clam)); - const bool distant = distant0 || aboveCount; + const bool maxDistant = distant || aboveCount; const bool magnify = maxDist < 1.0; - if (!distant0 && maxDist < 1.0) { // magnification - const float detailFrac = max(minLod, maxDist) - float(sharpen); - lodFrac = mix(float(distant), detailFrac, float(!clam)); - } else if (!distant || !clam) { - lodFrac = maxDist / pow(2, max(mip_base, 0)) - 1.0; - lodFrac = max(lodFrac, material.primLod.y); - tileOffset = mip_base; - } + const float detailFrac = max(minLod, maxDist) - float(sharpen); + const float magnifedFrac = mix(float(maxDistant), detailFrac, float(!clam)); + const float distantFrac = float(distant || (aboveCount && clam)); + const float notClampedFrac = max(maxDist / pow(2, max(mip_base, 0)) - 1.0, material.primLod.y); + + const float notMagnifedFrac = mix(distantFrac, notClampedFrac, !maxDistant || !clam); + lodFrac = mix(notMagnifedFrac, magnifedFrac, float(!distant && magnify)); + + uint tileOffset = mip_base * int(!(maxDistant && clam)); if (textLOD) { - tileOffset = distant ? material.mipCount : tileOffset; + tileOffset = maxDistant ? material.mipCount : tileOffset; if (detail) { - tileIndex1 = (tileIndex0 + tileOffset + (int(!(distant || magnify)) + 1)) & 7; + tileIndex1 = (tileIndex0 + tileOffset + (int(!(maxDistant || magnify)) + 1)) & 7; tileIndex0 = (tileIndex0 + tileOffset + int(!magnify)) & 7; } else { tileIndex0 = (tileIndex0 + tileOffset) & 7; tileIndex1 = tileIndex0; - if (!distant && (sharpen || !magnify)) - tileIndex1 = (tileIndex1 + 1) & 7; // Use next mip level + if (!maxDistant && (sharpen || !magnify)) + tileIndex1 = (tileIndex1 + 1) & 7; } } } From 065cb4a84f8555645ec46852799ddd4e0d494e0d Mon Sep 17 00:00:00 2001 From: Lila Date: Wed, 9 Jul 2025 17:30:15 +0100 Subject: [PATCH 08/16] mipBase --- shader/textures.glsl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shader/textures.glsl b/shader/textures.glsl index ce6b800..c003eea 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -97,22 +97,22 @@ void computeLOD( const vec2 dfd = max(dx, dy); const float maxDist = max(dfd.x, dfd.y); - const uint mip_base = uint(floor(log2(maxDist))); + const uint mipBase = uint(floor(log2(maxDist))); // TODO: should this value be scaled by clipping planes? const bool distant = perspectiveOverflow || maxDist >= 16384.0; - const bool aboveCount = mip_base >= material.mipCount; + const bool aboveCount = mipBase >= material.mipCount; const bool maxDistant = distant || aboveCount; const bool magnify = maxDist < 1.0; const float detailFrac = max(minLod, maxDist) - float(sharpen); const float magnifedFrac = mix(float(maxDistant), detailFrac, float(!clam)); const float distantFrac = float(distant || (aboveCount && clam)); - const float notClampedFrac = max(maxDist / pow(2, max(mip_base, 0)) - 1.0, material.primLod.y); + const float notClampedFrac = max(maxDist / pow(2, max(mipBase, 0)) - 1.0, material.primLod.y); const float notMagnifedFrac = mix(distantFrac, notClampedFrac, !maxDistant || !clam); lodFrac = mix(notMagnifedFrac, magnifedFrac, float(!distant && magnify)); - uint tileOffset = mip_base * int(!(maxDistant && clam)); + uint tileOffset = mipBase * int(!(maxDistant && clam)); if (textLOD) { tileOffset = maxDistant ? material.mipCount : tileOffset; From 396754b7a20a10b85d5140643d5df3301cc87dfa Mon Sep 17 00:00:00 2001 From: Lila Date: Wed, 9 Jul 2025 17:35:07 +0100 Subject: [PATCH 09/16] pass in more arguments --- shader/main3d.frag.glsl | 2 +- shader/textures.glsl | 12 ++++++------ shader/utils.glsl | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/shader/main3d.frag.glsl b/shader/main3d.frag.glsl index 02ee7e8..f5c0fba 100644 --- a/shader/main3d.frag.glsl +++ b/shader/main3d.frag.glsl @@ -155,7 +155,7 @@ void main() uint tex0Index = 0; uint tex1Index = 1; float lodFraction = 0.0; - computeLOD(tex0Index, tex1Index, material.primLod.y, dx, dy, false, lodFraction); + computeLOD(tex0Index, tex1Index, textLOD(), textDetail(), material.primLod.y, dx, dy, false, lodFraction); vec4 texData0 = sampleIndex(tex0Index, inputUV, texFilter); vec4 texData1 = sampleIndex(tex1Index, inputUV, texFilter); diff --git a/shader/textures.glsl b/shader/textures.glsl index c003eea..f413e54 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -82,14 +82,14 @@ vec4 sampleIndex(in const uint textureIndex, in const vec2 uvCoord, in const uin void computeLOD( inout uint tileIndex0, inout uint tileIndex1, - float minLod, - vec2 dx, - vec2 dy, - bool perspectiveOverflow, // this should be possible from what I've read in parallel-rdp, can always be removed + const bool textLOD, + const uint textDetail, + const float minLod, + const vec2 dx, + const vec2 dy, + const bool perspectiveOverflow, // this should be possible from what I've read in parallel-rdp, can always be removed out float lodFrac ) { - const bool textLOD = bool(textLOD()); - const uint textDetail = textDetail(); const bool sharpen = textDetail == G_TD_SHARPEN; const bool detail = textDetail == G_TD_DETAIL; const bool clam = textDetail == G_TD_CLAMP; diff --git a/shader/utils.glsl b/shader/utils.glsl index fc74b76..3e3f088 100644 --- a/shader/utils.glsl +++ b/shader/utils.glsl @@ -26,7 +26,7 @@ vec3 linearToGamma(in vec3 color) { #define cycleType() (OTHER_MODE_H & (3 << G_MDSFT_CYCLETYPE)) #define texFilter() (OTHER_MODE_H & (3 << G_MDSFT_TEXTFILT)) #define textPersp() (OTHER_MODE_H & (1 << G_MDSFT_TEXTPERSP)) -#define textLOD() (OTHER_MODE_H & (1 << G_MDSFT_TEXTLOD)) +#define textLOD() (bool(OTHER_MODE_H & (1 << G_MDSFT_TEXTLOD))) #define textDetail()(OTHER_MODE_H & (3 << G_MDSFT_TEXTDETAIL)) #define boolSelect(cond, a, b) (bool(mix(a, b, cond))) From 85e51d5c8f01dbdb5531d2b08132ccc3c346c9cc Mon Sep 17 00:00:00 2001 From: Lila Date: Wed, 9 Jul 2025 17:35:29 +0100 Subject: [PATCH 10/16] we only need this later --- shader/textures.glsl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shader/textures.glsl b/shader/textures.glsl index f413e54..19ce83d 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -112,10 +112,8 @@ void computeLOD( const float notMagnifedFrac = mix(distantFrac, notClampedFrac, !maxDistant || !clam); lodFrac = mix(notMagnifedFrac, magnifedFrac, float(!distant && magnify)); - uint tileOffset = mipBase * int(!(maxDistant && clam)); - if (textLOD) { - tileOffset = maxDistant ? material.mipCount : tileOffset; + const uint tileOffset = maxDistant ? material.mipCount : (mipBase * int(!(maxDistant && clam))); if (detail) { tileIndex1 = (tileIndex0 + tileOffset + (int(!(maxDistant || magnify)) + 1)) & 7; From 71b252296525cc6c2981f1783cb635a84c7040ce Mon Sep 17 00:00:00 2001 From: Lila Date: Wed, 9 Jul 2025 17:46:01 +0100 Subject: [PATCH 11/16] one less branch --- shader/textures.glsl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/shader/textures.glsl b/shader/textures.glsl index 19ce83d..1a59aa2 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -114,15 +114,15 @@ void computeLOD( if (textLOD) { const uint tileOffset = maxDistant ? material.mipCount : (mipBase * int(!(maxDistant && clam))); - + tileIndex0 = tileIndex0 + tileOffset; + tileIndex1 = tileIndex0; if (detail) { - tileIndex1 = (tileIndex0 + tileOffset + (int(!(maxDistant || magnify)) + 1)) & 7; - tileIndex0 = (tileIndex0 + tileOffset + int(!magnify)) & 7; + tileIndex1 += (int(!(maxDistant || magnify)) + 1); + tileIndex0 += int(!magnify); } else { - tileIndex0 = (tileIndex0 + tileOffset) & 7; - tileIndex1 = tileIndex0; - if (!maxDistant && (sharpen || !magnify)) - tileIndex1 = (tileIndex1 + 1) & 7; + tileIndex1 += uint(!maxDistant && (sharpen || !magnify)); } + tileIndex0 &= 7; + tileIndex1 &= 7; } } From f2f960e9416a8c5a815bb0eb29ddd3ad1a4d1a5b Mon Sep 17 00:00:00 2001 From: Lila Date: Wed, 9 Jul 2025 18:23:35 +0100 Subject: [PATCH 12/16] move comment --- shader/textures.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shader/textures.glsl b/shader/textures.glsl index 1a59aa2..314b57c 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -95,10 +95,10 @@ void computeLOD( const bool clam = textDetail == G_TD_CLAMP; const vec2 dfd = max(dx, dy); + // TODO: should this value be scaled by clipping planes? const float maxDist = max(dfd.x, dfd.y); const uint mipBase = uint(floor(log2(maxDist))); - // TODO: should this value be scaled by clipping planes? const bool distant = perspectiveOverflow || maxDist >= 16384.0; const bool aboveCount = mipBase >= material.mipCount; const bool maxDistant = distant || aboveCount; From 02aee94176215d4fe52e29b8129d8ec9e71a8a00 Mon Sep 17 00:00:00 2001 From: Lila Date: Wed, 16 Jul 2025 14:34:55 +0100 Subject: [PATCH 13/16] fix default tex size --- common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/common.py b/common.py index 9940bf0..ee4c94e 100644 --- a/common.py +++ b/common.py @@ -69,6 +69,7 @@ def get_scene_render_state(scene: bpy.types.Scene): convert=quantize_tuple(f64render_rs.default_convert, 9.0, -1.0, 1.0), cc=SOLID_CC, tex_confs=([get_tile_conf(getattr(f64render_rs, f"default_tex{i}")) for i in range(0, 8)]), + tex_size=(32, 32), ) state.lights[0] = F64Light( quantize_srgb(fast64_rs.light0Color, force_alpha=True), quantize_direction(fast64_rs.light0Direction) From 6938e43b182c4de49d42742186ece4fb908f9576 Mon Sep 17 00:00:00 2001 From: Lila Date: Sat, 26 Jul 2025 12:34:04 +0100 Subject: [PATCH 14/16] Update textures.glsl --- shader/textures.glsl | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/shader/textures.glsl b/shader/textures.glsl index 314b57c..90b8337 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -31,19 +31,18 @@ vec4 sampleSampler(in const sampler2D tex, in const TileConf tileConf, in vec2 u uvCoord -= tileConf.low; - const vec2 isClamp = step(tileConf.mask, vec2(1.0)); - const vec2 isMirror = step(tileConf.high, vec2(0.0)); - const vec2 isForceClamp = step(tileConf.mask, vec2(1.0)); // mask == 0 forces clamping - const vec2 mask = mix(abs(tileConf.mask), vec2(256), isForceClamp); // if mask == 0, we also have to ignore it + const vec2 isClamp = step(tileConf.mask, vec2(1.0)); // if mask is negated, clamp + const vec2 isMirror = step(tileConf.high, vec2(0.0)); // if high is negated, mirror + const vec2 mask = abs(tileConf.mask); const vec2 highMinusLow = abs(tileConf.high) - abs(tileConf.low); if (texFilter != G_TF_POINT) { uvCoord -= 0.5 * tileConf.shift; - const ivec2 texelBaseInt = ivec2(floor(uvCoord)); - const vec4 sample00 = wrappedMirrorSample(tex, texelBaseInt, mask, highMinusLow, isClamp, isMirror, isForceClamp); - const vec4 sample01 = wrappedMirrorSample(tex, texelBaseInt + ivec2(0, 1), mask, highMinusLow, isClamp, isMirror, isForceClamp); - const vec4 sample10 = wrappedMirrorSample(tex, texelBaseInt + ivec2(1, 0), mask, highMinusLow, isClamp, isMirror, isForceClamp); - const vec4 sample11 = wrappedMirrorSample(tex, texelBaseInt + ivec2(1, 1), mask, highMinusLow, isClamp, isMirror, isForceClamp); + const vec2 texelBaseInt = floor(uvCoord); + const vec4 sample00 = wrappedMirrorSample(tex, texelBaseInt, mask, highMinusLow, isClamp, isMirror); + const vec4 sample01 = wrappedMirrorSample(tex, texelBaseInt + vec2(0, 1), mask, highMinusLow, isClamp, isMirror); + const vec4 sample10 = wrappedMirrorSample(tex, texelBaseInt + vec2(1, 0), mask, highMinusLow, isClamp, isMirror); + const vec4 sample11 = wrappedMirrorSample(tex, texelBaseInt + vec2(1, 1), mask, highMinusLow, isClamp, isMirror); const vec2 fracPart = uvCoord - texelBaseInt; #ifdef USE_LINEAR_FILTER return quantizeTexture(tileConf.flags, mix(mix(sample00, sample10, fracPart.x), mix(sample01, sample11, fracPart.x), fracPart.y)); @@ -61,7 +60,7 @@ vec4 sampleSampler(in const sampler2D tex, in const TileConf tileConf, in vec2 u #endif } else { - return quantizeTexture(tileConf.flags, wrappedMirrorSample(tex, ivec2(floor(uvCoord)), mask, highMinusLow, isClamp, isMirror, isForceClamp)); + return quantizeTexture(tileConf.flags, wrappedMirrorSample(tex, ivec2(floor(uvCoord)), mask, highMinusLow, isClamp, isMirror)); } } From 12f010901a5328b97dc28fa7c6d5f088a2cc9828 Mon Sep 17 00:00:00 2001 From: Lila Date: Sat, 26 Jul 2025 20:14:35 +0100 Subject: [PATCH 15/16] minLod --- shader/textures.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shader/textures.glsl b/shader/textures.glsl index 90b8337..a470236 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -106,7 +106,7 @@ void computeLOD( const float detailFrac = max(minLod, maxDist) - float(sharpen); const float magnifedFrac = mix(float(maxDistant), detailFrac, float(!clam)); const float distantFrac = float(distant || (aboveCount && clam)); - const float notClampedFrac = max(maxDist / pow(2, max(mipBase, 0)) - 1.0, material.primLod.y); + const float notClampedFrac = max(maxDist / pow(2, max(mipBase, 0)) - 1.0, minLod); const float notMagnifedFrac = mix(distantFrac, notClampedFrac, !maxDistant || !clam); lodFrac = mix(notMagnifedFrac, magnifedFrac, float(!distant && magnify)); From 4852184d591f314d39dd89d7b02aa735f7a55490 Mon Sep 17 00:00:00 2001 From: Lila Date: Fri, 28 Nov 2025 18:46:06 +0000 Subject: [PATCH 16/16] update from main --- shader/textures.glsl | 25 ++++++++++++++++++++++++- shader/utils.glsl | 24 ------------------------ 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/shader/textures.glsl b/shader/textures.glsl index a470236..ad9893e 100644 --- a/shader/textures.glsl +++ b/shader/textures.glsl @@ -17,11 +17,34 @@ vec4 quantizeTexture(uint flags, vec4 color) { return flagSelect(flags, TEX_FLAG_MONO, colorQuant.rgba, colorQuant.rrrr); } +vec2 mirrorUV(const vec2 uvIn, const vec2 uvBound) +{ + vec2 uvMod2 = mod(uvIn, uvBound * 2.0 + 1.0); + return mix(uvMod2, (uvBound * 2.0) - uvMod2, step(uvBound, uvMod2)); +} + +vec4 wrappedMirrorSample(const sampler2D tex, vec2 uv, const vec2 mask, const vec2 highMinusLow, const vec2 isClamp, const vec2 isMirror) +{ + const ivec2 texSize = textureSize(tex, 0); + + // first apply clamping if enabled (clamp S/T, low S/T -> high S/T) + const vec2 uvClamp = clamp(uv, vec2(0.0), highMinusLow); + uv = mix(uv, uvClamp, isClamp); + + // then mirror the result if needed (mirror S/T) + const vec2 uvMirror = mirrorUV(uv, mask - 0.5); + uv = mix(uv, uvMirror, isMirror); + + // clamp again (mask S/T), this is also done to avoid OOB texture access + uv = mod(uv, min(texSize, mask)); + + return texelFetch(tex, ivec2(floor(uv)), 0); +} + vec4 sampleSampler(in const sampler2D tex, in const TileConf tileConf, in vec2 uvCoord, in const uint texFilter) { // https://github.com/rt64/rt64/blob/61aa08f517cd16c1dbee4e097768b08e2a060307/src/shaders/TextureSampler.hlsli#L156-L276 const ivec2 texSize = textureSize(tex, 0); - uvCoord.y = texSize.y - uvCoord.y; // invert Y uvCoord *= tileConf.shift; #ifdef SIMULATE_LOW_PRECISION diff --git a/shader/utils.glsl b/shader/utils.glsl index be6e45a..851d275 100644 --- a/shader/utils.glsl +++ b/shader/utils.glsl @@ -35,27 +35,3 @@ float noise(in vec2 uv) { return fract(sin(dot(uv, vec2(12.9898, 78.233)))* 43758.5453); } - -vec2 mirrorUV(const vec2 uvIn, const vec2 uvBound) -{ - vec2 uvMod2 = mod(uvIn, uvBound * 2.0 + 1.0); - return mix(uvMod2, (uvBound * 2.0) - uvMod2, step(uvBound, uvMod2)); -} - -vec4 wrappedMirrorSample(const sampler2D tex, vec2 uv, const vec2 mask, const vec2 highMinusLow, const vec2 isClamp, const vec2 isMirror) -{ - const ivec2 texSize = textureSize(tex, 0); - - // first apply clamping if enabled (clamp S/T, low S/T -> high S/T) - const vec2 uvClamp = clamp(uv, vec2(0.0), highMinusLow); - uv = mix(uv, uvClamp, isClamp); - - // then mirror the result if needed (mirror S/T) - const vec2 uvMirror = mirrorUV(uv, mask - 0.5); - uv = mix(uv, uvMirror, isMirror); - - // clamp again (mask S/T), this is also done to avoid OOB texture access - uv = mod(uv, min(texSize, mask)); - - return texelFetch(tex, ivec2(floor(uv)), 0); -}