20 分钟做出 3 个 Godot 着色器:2D 特效与 3D 材质快速上手
【免费下载链接】godotGodot Engine – Multi-platform 2D and 3D game engine项目地址: https://gitcode.com/GitHub_Trending/go/godot
想给角色轮廓加一层会呼吸的流光,或让一块石头被火"啃"掉一半?在 Godot 着色器里,这类效果往往只需要十几行代码。本文面向第一次接触着色器、完全不懂 GPU 与渲染管线的你,用约 20 分钟跑通 3 个可直接运行的效果:边缘泛光、溶解消失、水面涟漪。每段代码粘进编辑器立刻能看到画面变化,不用装任何插件。
五分钟先跑通一个效果
先不讲原理,给你一段不到 10 行的最小着色器,挂上去就能看到画面变化。
shader_type canvas_item; uniform vec4 tint : hint_color = vec4(0.2, 0.9, 1.0, 1.0); void fragment() { COLOR = texture(TEXTURE, UV) * tint; }操作路径:选中一个Sprite2D或ColorRect节点 → 在检查器顶部点"+"号 → 新建资源选ShaderMaterial→ 再给它的Shader属性新建Shader,把上面代码粘进去。保存后,画面里的图会被染成青色;你在检查器里拖动tint色块,颜色实时跟着变,不用重编译。
它到底怎么工作
读完这节能搞清着色器为什么"逐像素"算,以及 2D 和 3D 两类该怎么选。
把一张贴图想象成一堵由千万颗小方格拼成的马赛克墙。fragment()就是被派去给每一颗方格单独上色的工人:它对每个像素执行一次你写的几行代码,算出一个COLOR(2D)或ALBEDO(3D)写回去。成千上万个工人并行干活,所以哪怕你只写 4 行,画面也能丝滑地动起来。
canvas_item:管 2D,挂在Sprite2D、TextureRect、UI 控件上,输出COLOR。spatial:管 3D,挂在MeshInstance3D等网格的材质上,输出ALBEDO、ROUGHNESS等,还多一个改顶点位置的vertex()阶段。
记一句话就够:节点在哪个世界,就写哪种shader_type,这是后面 80% "效果不出来"问题的根源。
从零到成片
案例一:给贴图边缘加一层泛光
目标:让角色/图标的四周浮起一圈可控宽度的柔光。
shader_type canvas_item; uniform float glow_width : hint_range(0.0, 0.2) = 0.05; uniform vec4 glow_color : hint_color = vec4(0.3, 0.7, 1.0, 1.0); void fragment() { vec2 d = min(UV, vec2(1.0) - UV); float edge = min(d.x, d.y); float glow = smoothstep(glow_width, 0.0, edge); COLOR = texture(TEXTURE, UV) + glow_color * glow; }预期画面:贴图本体不变,外圈浮现一圈由内向外淡出的青光,像霓虹勾边。把glow_width从0.05调到0.15,光带明显变宽变淡;换glow_color成vec4(1.0,0.3,0.6,1.0)立刻变粉。
案例二:让模型溶解消失
目标:用一张噪声图"啃"掉网格,并留下一道发亮的溶解前沿。
shader_type spatial; render_mode cull_disabled; uniform float dissolve : hint_range(0.0, 1.0) = 0.0; uniform sampler2D noise_tex : hint_albedo; void fragment() { float n = texture(noise_tex, UV).r; if (n < dissolve) { discard; } float band = smoothstep(dissolve, dissolve + 0.1, n); ALBEDO = vec3(1.0, 0.35, 0.05) * band; }预期画面:把一张黑白噪声图拖进noise_tex,然后从 0 往 1 推dissolve,网格就像被火吞噬,碎块脱落,前沿留下一道橙亮边。dissolve + 0.1里的0.1控制亮边粗细,调成0.3边会更宽更"烧"。
案例三:让 3D 地面起涟漪
目标:用vertex()阶段抖动顶点高度,做出会流动的波纹水面。
shader_type spatial; uniform float ripple_speed : hint_range(0.0, 10.0) = 2.0; uniform float ripple_scale : hint_range(1.0, 20.0) = 8.0; void vertex() { float wave = sin(VERTEX.x * ripple_scale + TIME * ripple_speed) + sin(VERTEX.y * ripple_scale * 0.8 + TIME * ripple_speed * 1.3); VERTEX.z += wave * 0.05; } void fragment() { ALBEDO = vec3(0.1, 0.5, 0.8); ROUGHNESS = 0.2; }预期画面:一块带细分的平面会像水面一样起伏。ripple_speed调大 → 波纹流动更快;ripple_scale调大 → 波纹变密、波峰更碎;VERTEX.z += wave * 0.05里的0.05是振幅,调成0.2就是汹涌的大浪。
调出你要的样子
上面案例里反复出现这几个参数,记住它们"往哪边拧"会怎样:
glow_width(泛光宽度):调大 → 光带更宽更柔;调小 → 变成贴边的细线,调到 0 光几乎看不见。dissolve(溶解进度):调大 → 模型消失得越多;调到 1 会整块被discard吃空,记得留band前沿。ripple_speed(流动速度):调大 → 水面"跑"起来;调成 0 → 静止的一格定格波纹。ripple_scale(波纹密度):调大 → 波峰更密更碎;调小 → 只剩几道缓坡。ROUGHNESS(粗糙度):调小 → 表面更镜面、反光更锐利;调大 → 发雾、发哑。
翻车急救表
| 现象 / 报错 | 原因 | 解法 |
|---|---|---|
| 效果完全不显示,材质像没挂 | shader_type与节点维度不匹配(如spatial挂到 2D 节点) | 确认节点所属世界,2D 用canvas_item、3D 用spatial |
| 画面整片全黑 | 全部被discard吃掉,或COLOR/ALBEDO没赋值 | 检查是否if条件太苛刻,确保有像素能写出输出 |
| Inspector 里改参数没反应 | uniform名与代码不一致,或没暴露给材质 | 核对拼写完全一致,并确认节点挂的是ShaderMaterial |
| 掉帧、明显卡顿 | fragment()里有循环或多次texture()采样 | 减少采样次数、去掉循环,把常量提到循环外 |
红字unknown identifier | 语法/拼写错误,漏了分号或括号 | 看底部着色器面板标出的行号,逐行对齐括号 |
下一步去哪
想继续深挖,直接看仓库里这几处真实入口,比翻碎片教程更快:
- 可视化着色器模块:modules/visual_shader/,用节点连线做效果,适合不想手写代码的人。
- 着色器语言本体的实现:servers/rendering/shader_language.cpp,想弄清
TIME、UV这些内置变量从哪来就看它。 - 两个核心类的 API 说明:doc/classes/Shader.xml 与 doc/classes/ShaderMaterial.xml,方法名和参数以这里为准。
做完上面任意一个效果,不妨把它截图发出来,一起看看谁的泛光更漂亮、谁的涟漪更汹涌。
【免费下载链接】godotGodot Engine – Multi-platform 2D and 3D game engine项目地址: https://gitcode.com/GitHub_Trending/go/godot
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考