如何在Godot单着色器中灵活切换纹理过滤模式?
问题描述
我编写了一款为色盲用户设计的着色器,用于为物体添加图案效果(效果示例:物体表面覆盖可区分的图案纹理,帮助色盲用户识别不同物体)。
目前图案作为着色器参数传入,使用线性过滤:
uniform sampler2D pattern : filter_linear;
这款着色器需要同时应用于像素风格和平滑风格的物体——像素风格物体需要使用filter_nearest,平滑风格物体需要使用filter_linear。但Godot着色器语言似乎不支持通过着色器参数设置过滤类型,也无法使用文档中提到的filter_default关键字(该关键字可继承所应用节点的过滤设置)。
我能想到的解决办法只有编写两个不同的着色器,或者设置两个纹理输入参数,但这两种方案都不够理想。如何配置单个着色器来优雅地处理这两种过滤类型?
附上当前着色器代码:
shader_type canvas_item; uniform vec3 color1 = vec3(1.0, 1.0, 1.0); uniform vec3 color2 = vec3(1.0, 1.0, 1.0); uniform sampler2D pattern : filter_linear; uniform float patternScale = 3.; void fragment() { float textureW = float(textureSize(TEXTURE, 0).x); float textureH = float(textureSize(TEXTURE, 0).y); float patternW = float(textureSize(pattern, 0).x); float patternH = float(textureSize(pattern, 0).y); vec2 patternCoordinates = vec2(mod(UV.x * textureW / patternW / patternScale, 1.), mod(UV.y * textureH / patternH / patternScale, 1.)); float patternValue = 1. - texture(pattern, patternCoordinates).r; vec4 color = COLOR; vec3 patternColor = (color.a > .7 ? (color1 * (1. - patternValue) + color2 * patternValue) : color1); color.r *= patternColor.r; color.g *= patternColor.g; color.b *= patternColor.b; COLOR = color; }
解决方案
可以通过手动实现纹理采样逻辑,配合开关参数来切换过滤模式,无需编写多个着色器或额外纹理参数。具体做法是:在着色器中同时实现线性过滤与最近邻采样的逻辑,用一个uniform参数控制使用哪一种结果。
修改后的完整着色器代码:
shader_type canvas_item; uniform vec3 color1 = vec3(1.0, 1.0, 1.0); uniform vec3 color2 = vec3(1.0, 1.0, 1.0); uniform sampler2D pattern : filter_linear; uniform float patternScale = 3.; uniform bool use_nearest_filter = false; // 过滤模式切换开关 void fragment() { float textureW = float(textureSize(TEXTURE, 0).x); float textureH = float(textureSize(TEXTURE, 0).y); float patternW = float(textureSize(pattern, 0).x); float patternH = float(textureSize(pattern, 0).y); vec2 patternCoordinates = vec2(mod(UV.x * textureW / patternW / patternScale, 1.), mod(UV.y * textureH / patternH / patternScale, 1.)); float patternValue; if (use_nearest_filter) { // 手动计算最近邻采样坐标 vec2 nearest_uv = floor(patternCoordinates * vec2(patternW, patternH)) / vec2(patternW, patternH); patternValue = 1. - texture(pattern, nearest_uv).r; } else { // 默认线性过滤采样 patternValue = 1. - texture(pattern, patternCoordinates).r; } vec4 color = COLOR; vec3 patternColor = (color.a > .7 ? (color1 * (1. - patternValue) + color2 * patternValue) : color1); color.r *= patternColor.r; color.g *= patternColor.g; color.b *= patternColor.b; COLOR = color; }
关键说明
- 添加
use_nearest_filter布尔参数,勾选时启用最近邻采样适配像素风格,取消勾选则保持线性过滤用于平滑风格 - 最近邻采样通过将UV坐标对齐到纹理像素边界实现:先把UV乘纹理分辨率取整,再除以分辨率,避免线性插值产生的模糊效果
- 该方案无需改动纹理资源的过滤设置,所有逻辑封装在单个着色器内,切换模式仅需调整参数即可
若想尝试继承节点的纹理过滤设置,可移除着色器中pattern变量的filter_linear限定符,并将图案纹理的过滤模式设为Default。但部分Godot版本对filter_default的支持存在兼容性问题,手动实现采样的方式稳定性更高。
内容的提问来源于stack exchange,提问作者4444
相关产品推荐
相关产品推荐

