着色器 (Shader) 的基本概念和 GLSL 语法 笔记

一、着色器的基本概念

着色器是用一种类似 C 的语言(GLSL)编写的、在 GPU 上运行的小程序。它们不是你写的传统"应用代码",而是专门为处理图形渲染管线中的特定阶段而设计的。

核心思想 :传统的固定渲染管线是"黑盒",你只能配置参数。而可编程渲染管线允许你编写这些小程序,从而完全控制顶点如何变换、像素如何着色的过程,实现无限可能的效果。

在 OpenGL ES 2.0/3.0 中,最重要的两个可编程着色器是:

  1. 顶点着色器:

    • 执行频率 :每个顶点执行一次。
    • 核心任务 :
      • 坐标变换:将顶点从模型空间,经过模型、视图、投影矩阵变换,最终到屏幕的裁剪空间。
      • 传递数据:计算并输出后续阶段(如片段着色器)所需的数据,如颜色、纹理坐标、法线等。
  2. 片段着色器:

    • 执行频率 :每个片段(可粗略理解为像素候选)执行一次。
    • 核心任务 :
      • 决定最终颜色:计算该片段的最终颜色值。这包括纹理采样、光照计算、颜色混合等所有决定像素外观的操作。

渲染流程简化视图:

rust 复制代码
顶点数据 -> [顶点着色器](处理每个顶点)-> 图元装配/光栅化 -> [片段着色器](处理每个片段)-> 测试与混合 -> 屏幕像素

二、GLSL 语法核心要点

GLSL(OpenGL Shading Language)是为图形计算量身定制的语言,语法类似C,但有显著的不同。

1. 版本声明

必须是第一行(注释除外)。OpenGL ES 的 GLSL 有特殊版本号。

glsl 复制代码
// OpenGL ES 2.0 对应 GLSL ES 1.00
#version 100 es

// OpenGL ES 3.0 对应 GLSL ES 3.00
#version 300 es

2. 变量类型与限定符

这是 GLSL 最关键的特性之一,用于定义数据的来源、去向和性质。

  • attribute(仅用于顶点着色器,ES 2.0) / in(ES 3.0):

    • 定义逐顶点的输入数据。每个顶点可以有不同的值。
    • 例如:顶点位置、顶点颜色、顶点法线、纹理坐标。
    glsl 复制代码
    // ES 2.0
    attribute vec4 aPosition;
    attribute vec2 aTexCoord;
    // ES 3.0
    in vec4 aPosition;
  • uniform:

    • 定义全局 、恒定 的输入变量。在一次绘制调用中,所有顶点和片段访问到的 uniform 值都是相同的。
    • 例如:变换矩阵、光源位置、颜色、时间。
    glsl 复制代码
    uniform mat4 uMVPMatrix; // 模型-视图-投影矩阵
    uniform float uTime;
  • varying(ES 2.0) / out(顶点着色器) & in(片段着色器,ES 3.0):

    • 用于从顶点着色器向片段着色器传递数据。
    • 顶点着色器为每个顶点计算出该值,在光栅化过程中,这些值会被插值 ,然后片段着色器收到的是插值后的、对应于当前片段的值。
    • 例如:将顶点颜色、纹理坐标、从顶点着色器计算出的光照强度传递给片段着色器。
    glsl 复制代码
    // ES 2.0
    // 顶点着色器中
    varying vec2 vTexCoord;
    // 片段着色器中
    varying vec2 vTexCoord;
    
    // ES 3.0
    // 顶点着色器中
    out vec2 vTexCoord;
    // 片段着色器中
    in vec2 vTexCoord;
  • 精度限定符:

    • GLSL ES 特有,用于指定变量的计算精度,平衡速度和资源。片段着色器中必须声明浮点数精度。
    • lowp(低精度)、mediump(中精度)、highp(高精度)。
    glsl 复制代码
    precision mediump float; // 在片段着色器开头声明默认浮点数精度
    uniform highp sampler2D uTexture; // 纹理采样器通常需要高精度
    varying lowp vec4 vColor; // 颜色数据可能用低精度就够了

3. 基本数据类型

  • float, int, bool: 标量。
  • vec2, vec3, vec4: 包含2、3、4个浮点数的向量。常用于表示位置、颜色(RGBA)、纹理坐标。
  • mat2, mat3, mat4: 2x2, 3x3, 4x4 浮点数矩阵。
  • sampler2D, samplerCube: 纹理采样器,是用于从纹理中读取数据的特殊类型。

4. 内置变量与函数

  • 顶点着色器输出 :

    glsl 复制代码
    vec4 gl_Position; // 必须赋值!表示顶点在裁剪空间中的最终位置。
    float gl_PointSize; // 可选,绘制点精灵时的大小。
  • 片段着色器输出 :

    glsl 复制代码
    // ES 2.0
    vec4 gl_FragColor; // 必须赋值!表示该片段的最终颜色。
    // ES 3.0
    out vec4 oFragColor; // 自定义输出变量,可以多个(用于多渲染目标)。
  • 内置函数 : GLSL 提供了丰富的数学和图形函数。

    • 数学: sin, cos, pow, sqrt, abs
    • 几何: dot(点积), cross(叉积), normalize(归一化), length(长度)
    • 纹理: texture2D(ES 2.0) / texture(ES 3.0)(纹理采样)

三、一个完整的简单例子 (OpenGL ES 2.0)

顶点着色器 (vertex_shader.glsl):

glsl 复制代码
#version 100 es // ES 2.0
// 输入:顶点的属性
attribute vec4 aPosition;
attribute vec4 aColor;
// 输出:传递给片段着色器的变量(会被插值)
varying vec4 vColor;
// 全局常量:变换矩阵
uniform mat4 uMVPMatrix;

void main() {
    // 核心任务:计算裁剪空间坐标
    gl_Position = uMVPMatrix * aPosition;
    // 将顶点颜色传递给片段着色器
    vColor = aColor;
}

片段着色器 (fragment_shader.glsl):

glsl 复制代码
#version 100 es
// ES 2.0 片段着色器必须声明默认浮点精度
precision mediump float;
// 输入:从顶点着色器传来(经过插值)
varying vec4 vColor;

void main() {
    // 核心任务:决定最终颜色
    // 这里简单地使用插值后的颜色
    gl_FragColor = vColor;
}

四、OpenGL ES 3.0 的关键语法变化

  1. in/out 取代 attribute/varying:声明更清晰,统一了顶点和片段着色器的输入输出语法。
  2. 自定义片段输出 :不再使用 gl_FragColor,而是自己声明 out vec4 outColor;。
  3. 纹理函数统一 :texture2D, textureCube 等函数统一为 texture 函数。
  4. 更严格的语法:例如,不再支持数组和循环的不确定索引(除非使用特殊限定符)。

ES 3.0 等价示例片段:

glsl 复制代码
// 顶点着色器开头
#version 300 es
in vec4 aPosition;
out vec4 vColor;
// 片段着色器开头
#version 300 es
precision mediump float;
in vec4 vColor;
out vec4 outColor; // 自定义输出

五、调试与最佳实践

  • 错误检查 :在C++/Java端,务必 在编译链接着色器后,使用 glGetShaderInfoLog 和 glGetProgramInfoLog 获取错误信息。这是调试着色器问题的唯一可靠途径。
  • 从简单开始:先让一个最简单的着色器(如直接输出颜色)工作,再逐步增加复杂度(矩阵、纹理、光照)。
  • 注意精度 :在移动设备上,不当的精度选择可能导致性能下降或渲染瑕疵。通常 mediump 是安全且高效的起点。
  • 避免条件分支 :GPU 擅长并行处理相同指令流,复杂的 if-else 或循环会显著降低性能。尽量使用步进函数、混合等方式替代。
相关推荐
三少爷的鞋19 分钟前
别再这么写协程了!Marcin Moskała 剖析的 几个常见协程误区与重构
android
mmsx2 小时前
Android 防二次打包第一道锁:签名校验与完整性校验
android·kotlin
mmsx2 小时前
Android 混淆不等于安全:二次打包链路完整走一遍
android·kotlin
小宋10214 小时前
Agent轨迹级评测实战:工具选择、预算超限与回归门禁
android·网络·人工智能·回归
墨天梦5 小时前
D05_ViewModel与单向数据流
android·kotlin
蒸鱼Yuzheng7 小时前
Android 构建可复现性:APK 指纹、文件级差异与供应链审计
android·apk·devops·软件供应链·可复现构建
墨天梦9 小时前
D03_Compose列表与稳定身份
android·gitee·kotlin
萌新杰少9 小时前
Kuikly股票查看软件开发体验——SaiRen
android·kotlin·客户端
vilya10 小时前
把 Python 塞进 APK:Chaquopy 打包实践
android·python
用户928172673901610 小时前
Android Compose版本的AI组件库来了。
android·kotlin