如何在Godot着色器中采样任意UI控件的COLOR像素?
Godot画布项着色器:通用UI元素波浪效果实现问题
问题描述
在Godot画布项片段着色器中,我梳理了以下特性:
COLOR存储当前画布项的像素颜色(例如白色文本的像素为白色)TEXTURE对应控件的纹理,但带文本的默认按钮这类控件未设置该值(显示为纯白色)hint_screen_texture对应的屏幕纹理仅包含已渲染元素,当前着色器正在处理的内容尚未写入,且会包含下方元素,不符合需求
我编写了一段实现波浪按钮效果的着色器代码:
shader_type canvas_item; uniform float wave_speed: hint_range(0.0, 5.0, 0.01) = 0.1; uniform float wave_count: hint_range(0.0, 25.0, 1.0) = 10.0; uniform float wave_size: hint_range(0.0, 1.0, 0.01) = 0.1; void fragment() { // shrink by wave_size so we have space for the effect float remainingSize = 1.0 - wave_size; vec2 newUV = UV / vec2(remainingSize, 1.0) - vec2(wave_size, 0.0); // actual wave effect float halfWaveSize = wave_size * 0.5; newUV += vec2(sin((UV.y + TIME * wave_speed) * PI * wave_count) * halfWaveSize + halfWaveSize, 0.0); COLOR = newUV.x < 0.0 || newUV.x > 1.0 ? vec4(0.0) : texture(TEXTURE, newUV); }
这段代码在TextureRect上可正常工作,但在默认按钮上仅显示波浪状白块。请问是否存在方法,能对任意UI元素(纹理、文本、形状等)的指定UV坐标采样COLOR?
解决方案
核心问题分析
你的着色器依赖TEXTURE采样像素,但默认按钮这类复杂UI控件的视觉内容(文本、背景)并非通过自身的TEXTURE绑定实现,而是通过Godot的样式系统或子节点绘制,直接采样TEXTURE只能得到纯白结果。
可靠实现方案:SubViewport预渲染
要让着色器作用于整个UI元素的完整视觉内容,最有效的方式是先将UI元素预渲染为纹理,再对该纹理应用波浪效果:
- 创建
SubViewport,设置其尺寸与目标UI元素匹配(或开启Size Flags自适应)。 - 将目标UI元素(如默认按钮)作为
SubViewport的子节点。 - 创建
TextureRect,将其Texture属性设置为SubViewport的GetTexture()返回值。 - 给
TextureRect应用你的波浪着色器。
这种方式下,TextureRect的TEXTURE就是整个UI元素的完整渲染结果,着色器能正常采样到包含文本、背景的所有像素,实现预期的波浪效果。
兼容无纹理控件的着色器修改(局限性大)
若不想使用预渲染,可修改着色器兼容无纹理控件,但仅适用于纯色或单一形状的控件,无法处理包含子节点(如按钮文本)的复杂UI:
shader_type canvas_item; uniform float wave_speed: hint_range(0.0, 5.0, 0.01) = 0.1; uniform float wave_count: hint_range(0.0, 25.0, 1.0) = 10.0; uniform float wave_size: hint_range(0.0, 1.0, 0.01) = 0.1; void fragment() { float remainingSize = 1.0 - wave_size; vec2 newUV = UV / vec2(remainingSize, 1.0) - vec2(wave_size, 0.0); float halfWaveSize = wave_size * 0.5; newUV += vec2(sin((UV.y + TIME * wave_speed) * PI * wave_count) * halfWaveSize + halfWaveSize, 0.0); if (newUV.x < 0.0 || newUV.x > 1.0 || newUV.y < 0.0 || newUV.y > 1.0) { COLOR = vec4(0.0); } else { vec4 texColor = texture(TEXTURE, newUV); // 检测是否为无纹理的纯白状态 bool isDefaultTexture = texColor.r == 1.0 && texColor.g == 1.0 && texColor.b == 1.0 && texColor.a == 1.0; COLOR = isDefaultTexture ? COLOR : texColor * COLOR; } }
内容的提问来源于stack exchange,提问作者Lahzey
相关产品推荐
相关产品推荐

