N64 RDP Texture Filtering Operations
The main goal of this shader code is to attempt to replicate the distinctive visual characteristics of the Nintendo 64’s Texture filtering:
- 3-Point Bilinear Sampling
- “Average” Sampling / “True” Bilinear
ShaderInclude written using angrylion-rdp-plus as a point of reference.
A Simple shader code is included showing how to make use of the filter operations.
ShaderInclude:
ivec2 get_texture_size(sampler2D tex) {
return textureSize(tex, 0);
}
ivec2 wrap_texel(ivec2 coord, ivec2 size) {
return ivec2(
(coord.x % size.x + size.x) % size.x,
(coord.y % size.y + size.y) % size.y
);
}
#ifdef FILTER_NONE
vec4 sample_filter(sampler2D tex, vec2 uv) {
return texture(tex, uv);
}
#endif
#ifdef FILTER_3POINT
vec4 sample_filter(sampler2D tex, vec2 uv) {
ivec2 texture_size = get_texture_size(tex);
vec2 pixel_coord_float = uv * vec2(texture_size) - 0.5;
ivec2 texel_coord = ivec2(floor(pixel_coord_float));
vec2 frag_coord = fract(pixel_coord_float);
vec4 t0 = texelFetch(tex, wrap_texel(texel_coord, texture_size), 0);
vec4 t1 = texelFetch(tex, wrap_texel(texel_coord + ivec2(1, 0), texture_size), 0);
vec4 t2 = texelFetch(tex, wrap_texel(texel_coord + ivec2(0, 1), texture_size), 0);
vec4 t3 = texelFetch(tex, wrap_texel(texel_coord + ivec2(1, 1), texture_size), 0);
if (frag_coord.x + frag_coord.y < 1.0) {
return t0 + (t1 - t0) * frag_coord.x + (t2 - t0) * frag_coord.y;
} else {
vec2 weights = vec2(1.0 - frag_coord.x, 1.0 - frag_coord.y);
return t3 + (t2 - t3) * weights.x + (t1 - t3) * weights.y;
}
}
#endif
#ifdef FILTER_BOX
vec4 sample_filter(sampler2D tex, vec2 uv) {
ivec2 texture_size = get_texture_size(tex);
vec2 pixel_coord = uv * vec2(texture_size) - 0.5;
ivec2 texel_coord = ivec2(floor(pixel_coord));
vec2 f = fract(pixel_coord);
vec4 t0 = texelFetch(tex, wrap_texel(texel_coord, texture_size), 0);
vec4 t1 = texelFetch(tex, wrap_texel(texel_coord + ivec2(1, 0), texture_size), 0);
vec4 t2 = texelFetch(tex, wrap_texel(texel_coord + ivec2(0, 1), texture_size), 0);
vec4 t3 = texelFetch(tex, wrap_texel(texel_coord + ivec2(1, 1), texture_size), 0);
return mix(mix(t0, t1, f.x), mix(t2, t3, f.x), f.y);
}
#endif
Shader code
shader_type canvas_item;
// Only one of these should be defined at a time
//#define FILTER_NONE
#define FILTER_3POINT
//#define FILTER_BOX
#include "res://location-to/texture_filter_operations.gdshaderinc"
void fragment() {
COLOR = sample_filter(TEXTURE, UV);
}



