互动教程

HLSL 着色器基础

从零开始学习GPU编程,掌握实时图形学的核心技能

🎨 可视化学习
实时演示
📝 手把手教学
观看原视频教程

如何运行着色器代码?

4种简单的方法来实验本教程中的代码

📺 原视频教程

本教程基于 Introduction to shaders: Learn the basics! (The Coding Train) 制作。

建议配合视频学习,效果更佳!

在 YouTube 上观看
💡 推荐方式:在线编辑器(最快入门)

无需安装任何软件,打开浏览器即可开始编写着色器!

方法 1
  • 最适合初学者
  • 实时预览效果
  • 内置大量示例
  • 支持错误提示
使用步骤:
  1. 打开链接
  2. 在左侧编辑器中粘贴代码
  3. 右侧自动显示结果
方法 2
  • 全球最大的着色器社区
  • 海量创意作品
  • 支持WebGL/GLSL
  • 可分享和fork代码
注意事项:

Shadertoy使用uniform变量名略有不同(如iTime代替u_time

方法 3
  • 简洁的在线编辑器
  • 支持顶点和片段着色器
  • 可下载为HTML
  • 适合快速测试
方法 4
本地环境:p5.js
  • 适合有编程基础的开发者
  • JavaScript创意编程库
  • 内置着色器支持
  • 可离线使用
// 安装 p5.js npm install p5 // 或直接在HTML中引入 <script src="p5.min.js"></script>
⚡ 快速开始示例

复制以下代码到 The Book of Shaders Editor 中立即查看效果:

#ifdef GL_ES precision mediump float; #endif uniform float u_time; uniform vec2 u_resolution; void main() { // 归一化坐标到 0.0 - 1.0 vec2 st = gl_FragCoord.xy / u_resolution.xy; // 创建红色渐变 vec3 color = vec3(st.x, 0.0, 0.0); // 添加时间动画 color.g = sin(u_time) * 0.5 + 0.5; gl_FragColor = vec4(color, 1.0); }
📚 本教程代码说明

本教程展示的GLSL代码是基于 GLSL ES(OpenGL着色语言嵌入式版本),这是WebGL和移动设备使用的标准。

  • 着色器类型:片段着色器(Fragment Shader)
  • 内置变量
    • gl_FragCoord:当前像素坐标
    • gl_FragColor:输出颜色(老版本GLSL)
  • 常用uniform变量
    • u_time:运行时间(秒)
    • u_resolution:画布分辨率
    • u_mouse:鼠标位置
🎯 学习建议
  1. 边学边练:每个章节都尝试修改代码参数
  2. 实验心态:着色器编程充满惊喜,大胆尝试
  3. 社区资源:Shadertoy上有海量优秀作品可以学习
  4. 调试技巧:使用颜色来可视化变量值

什么是着色器?

理解GPU编程的基础概念

概念 1.1
CPU vs GPU

着色器是运行在GPU(图形处理单元)而非CPU上的代码。

  • CPU核心:少而强,适合串行处理
  • GPU核心:数百个核心,擅长并行处理
  • 并行计算:同时处理大量数据
// CPU: 串行处理 for (pixel in pixels) { process(pixel); // 一个接一个 } // GPU: 并行处理 processAll(pixels); // 同时处理所有像素
概念 1.2
着色器类型

着色器最常见的两种类型:

  • 顶点着色器(Vertex Shader):处理形状的顶点位置
  • 片段着色器(Fragment Shader):处理每个像素的颜色
// 工作流程 CPU → 顶点数据 → 顶点着色器 → 屏幕位置 ↓ 片段着色器 → 像素颜色 → 显示
概念 1.3
为什么使用着色器?

性能优势:

  • 1280×720屏幕 = 近100万像素
  • 每秒60帧 = 每秒6000万次计算
  • GPU可以轻松处理,CPU会很慢

GLSL 编程基础

着色器语言的核心概念

概念 2.1
数据类型

GLSL是强类型语言,必须声明变量类型。

// 整数 int x = 10; // 浮点数(着色器常用) float y = 3.14; // 向量(2-4个分量) vec2 pos2 = vec2(1.0, 2.0); vec3 pos3 = vec3(1.0, 2.0, 3.0); vec4 color = vec4(1.0, 0.0, 0.0, 1.0);
概念 2.2
向量分量访问

向量分量可以用多种方式访问:

vec3 pos = vec3(1.0, 2.0, 3.0); // 位置坐标 float x = pos.x; float y = pos.y; float z = pos.z; // 颜色分量 float r = pos.r; float g = pos.g; float b = pos.b;
概念 2.3
Swizzling(重组)

提取和重排向量分量:

vec3 pos = vec3(1.0, 2.0, 3.0); // 提取 x 和 y vec2 xy = pos.xy; // 重排:交换红色和蓝色 vec4 color = vec4(1.0, 0.0, 0.0, 1.0); vec4 swapped = color.bgra; // (1,0,0,1)
🎮 互动演示:向量练习

变量类型详解

属性、统一变量和varying变量

概念 3.1
Attributes(属性)
  • 只在顶点着色器中可用
  • 每个顶点都有不同的值
  • 例如:顶点位置、纹理坐标
attribute vec3 position; attribute vec2 texCoord;
概念 3.2
Uniforms(统一变量)
  • 在整个着色器运行期间保持不变
  • 在顶点和片段着色器中都可用
  • 从CPU传递数据到着色器
  • 只读,不能在着色器内修改
uniform float millis; // 时间 uniform sampler2D tex; // 纹理 uniform vec3 color; // 颜色
概念 3.3
Varyings(传递变量)
  • 在顶点着色器中设置
  • 传递给片段着色器(只读)
  • 用于顶点到片段的数据传递
// 顶点着色器 varying vec2 vTexCoord; // 片段着色器 varying vec2 vTexCoord; // 接收

归一化与颜色

理解着色器中的数值范围

⚠️ 重要概念:归一化

着色器中的许多值都是归一化的,即范围在 0 到 1之间。

// 传统颜色 (0-255) r = 255, g = 0, b = 0 // 着色器颜色 (0.0-1.0) r = 1.0, g = 0.0, b = 0.0 // 白色 = 所有通道为 1.0 vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
🎨 互动演示:颜色混合器

创建渐变效果

使用位置信息创建颜色渐变

技巧 5.1
线性渐变(1D)

使用x坐标创建水平渐变:

// position.x 范围: 0.0 到 1.0 vec4 color = vec4(pos.x, 0.0, 0.5, 1.0);
技巧 5.2
二维渐变(2D)

同时使用x和y坐标:

// 红色随x增加,绿色随y增加 vec4 color = vec4(pos.x, pos.y, 0.0, 1.0);
技巧 5.3
mix() 函数

在两个值之间线性插值:

vec4 color1 = vec4(0.0, 0.0, 1.0, 1.0); // 蓝色 vec4 color2 = vec4(1.0, 0.0, 1.0, 1.0); // 粉色 // 使用mix创建渐变 vec4 gradient = mix(color1, color2, pos.x);
🌈 互动演示:渐变生成器

GLSL 内置函数

常用数学函数详解

函数 6.1
fract() - 小数部分

返回数的小数部分,用于创建重复图案。

  • fract(0.7) = 0.7
  • fract(1.7) = 0.7
vec2 newPos = fract(pos * 10.0);
函数 6.2
sin() - 正弦函数

创建波浪效果,注意需要重新映射到0-1范围。

// sin() 返回 -1.0 到 1.0 float wave = sin(pos.x * 10.0); // 重新映射到 0.0 - 1.0 float color = wave * 0.5 + 0.5;
函数 6.3
step() - 阶梯函数

类似于if语句,但更适合GPU并行处理。

// 如果 value < edge,返回 0,否则返回 1 float result = step(0.5, value); // 用于创建锐利的边缘 float circle = step(0.0, distance);

纹理与图像处理

在着色器中使用纹理

GLSL
// 声明纹理统一变量
uniform sampler2D background;

void main() {
    vec2 pos = vTexCoord;  // 获取纹理坐标

    // 读取纹理颜色(翻转Y轴)
    vec2 newTexCoord = vec2(pos.x, 1.0 - pos.y);
    vec4 color = texture2D(background, newTexCoord);

    // 输出颜色
    gl_FragColor = color;
}
💡 灰度效果实现
// 计算RGB平均值 float average = (color.r + color.g + color.b) / 3.0; // 将平均值应用到所有通道 vec4 grayscale = vec4(average, average, average, color.a);

SDF 有符号距离场

绘制形状的高级技术

🎯 什么是SDF?

SDF(Signed Distance Function)返回点到形状表面的距离:

  • 距离 > 0:点在形状外部
  • 距离 = 0:点在形状边缘
  • 距离 < 0:点在形状内部
GLSL
// 圆的SDF
vec3 circle = vec3(0.5, 0.5, 0.3);

// 计算到中心的距离
float dist = length(pos.xy - circle.xy);

// 减去半径,得到SDF
float sdf = dist - circle.z;

// 使用step函数创建锐利的圆
float circleShape = step(0.0, sdf);

gl_FragColor = vec4(circleShape);
🎯 互动演示:SDF 圆形生成器