📘 Kanzi Shader 核心笔记


一、基本概念与工作流程

1.1 Shader 是什么?

  • Shader 是运行在 GPU 上的小程序,用 GLSL (OpenGL Shading Language) 编写。

  • 两种必须的 Shader:

    • 顶点着色器 (Vertex Shader):处理顶点位置/法线等。

    • 片段着色器 (Fragment Shader):计算每个像素颜色。

  • Kanzi 支持 OpenGL ES 2.0 (#version 100) 和 ES 3.0 (#version 300 es)。

1.2 Shader 在 Kanzi 中的位置

  • 源码必须放在 Projects/<项目名>/Shaders 目录下,Studio 自动索引。

  • 通过 材质类型 (Material Type) 关联 Shader,再通过 材质 (Material) 给模型/UI赋值。

  • 渲染流程:网格 (Mesh) → 材质 (Material) → 材质类型 (Material Type) → Shader 代码。


二、GLSL 语法核心(Kanzi 环境)

2.1 文件结构必备要素

#version 100          // 必须首行,声明 GLSL 版本
precision mediump float; // 片段着色器必须声明默认精度

// uniform / attribute / varying 声明...

void main() {
    // 你的代码
}

2.2 常用数据类型

类型说明示例
float单精度浮点0.5
vec22 分量向量UV 坐标 (u, v)
vec33 分量向量颜色 (r,g,b) 或方向 (x,y,z)
vec44 分量向量颜色+透明度 (r,g,b,a) 或齐次坐标 (x,y,z,w)
mat33×3 矩阵法线变换矩阵
mat44×4 矩阵世界/视图/投影矩阵
sampler2D2D 纹理采样器声明贴图变量

2.3 变量修饰符(极重要)

修饰符用途应用阶段Kanzi 对应
attribute (ES2) / in (ES3)从网格传入顶点数据,每个顶点不同顶点着色器kzPosition
uniformCPU 传入,一次绘制内所有顶点/像素相同两种着色器都可材质属性、矩阵
varying (ES2) / out (顶点) in (片段)(ES3)从顶点着色器插值传到片段着色器两阶段间传递自定义 UV、颜色
精度 highp/mediump/lowp声明浮点计算精度,片段着色器需默认声明片段着色器precision mediump float;

Kanzi 习惯:公司代码可能 ES2 和 ES3 混用,记住 varying ≈ in/out 就行。


三、Kanzi 内置变量(不需要声明,直接使用)

所有以 kz 开头的变量都是引擎自动注入的,绝不可自行声明

3.1 顶点着色器输入(从网格传入)

变量名类型说明
kzPositionvec3顶点局部坐标,必须使用
kzNormalvec3顶点法线
kzTextureCoordinate0vec2第一套 UV(最常用)
kzTextureCoordinate1vec2第二套 UV(如果有)
kzColorvec4顶点颜色(通常来自笔刷)
kzTangentvec3/vec4切线,法线贴图需要

3.2 内置 Uniform(变换矩阵 & 相机)

变量名类型作用
kzWorldMatrixmat4模型 → 世界空间
kzViewMatrixmat4世界 → 相机(视图)空间
kzProjectionMatrixmat4相机 → 裁剪空间(投影)
kzWorldViewProjectionMatrixmat4三合一,最常用。直接把局部坐标转到裁剪空间
kzNormalMatrixmat3用于法线的正确变换(世界矩阵的逆转置)
kzCameraPositionvec3相机在世界空间的位置

3.3 必须输出的变量

变量用途示例
gl_Position (顶点着色器)顶点最终裁剪坐标,必赋值gl_Position = kzWorldViewProjectionMatrix * vec4(kzPosition, 1.0);
gl_FragColor (片段着色器,ES2)像素最终颜色gl_FragColor = vec4(1.0,0.0,0.0,1.0);
自定义 out vec4 (ES3)替代 gl_FragColorout vec4 FragColor; 然后赋值 FragColor = ...

四、自定义 Uniform:让 Studio 控制 Shader

4.1 Shader 中声明

uniform vec4 MyColor;
uniform sampler2D MyTexture;
  • 命名规则:不要用 kz 前缀,用小写字母开头即可。

4.2 Studio 中绑定

  1. 找到对应 Material Type。

  2. 在 Property Types 里创建同名属性,选对类型(如 Color 绑 vec4,Texture 绑 sampler2D)。

  3. 在具体 Material 中给属性赋值(选颜色、拖图片)。
    这样数据就自动传入 Shader。


五、高频内置函数(UI Shader 精华)

函数作用常用场景
texture2D(sampler, uv) (ES2) / texture(sampler, uv) (ES3)纹理采样,返回 vec4 颜色获取贴图颜色
step(edge, x)x < edge 返回 0,否则 1硬边遮罩
smoothstep(edge0, edge1, x)平滑过渡,抗锯齿边缘进度条边缘、光晕、渐变
mix(a, b, t)线性插值 a*(1-t) + b*t纹理混合、颜色切换
clamp(x, min, max)钳制范围防止数值溢出
dot(a, b)向量点积漫反射光照 N·L;灰度计算
length(v)向量长度制作圆形遮罩
normalize(v)向量归一化方向单位化
pow(x, y)幂运算Gamma 校正、高光

实用套路举例

// 进度条平滑边缘
float alpha = smoothstep(progress, progress+0.02, uv.x);
// 圆形裁剪
float dist = length(uv - 0.5);
float mask = 1.0 - smoothstep(radius-0.01, radius, dist);
// 灰度化
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));

六、UV 与多套纹理坐标

6.1 UV 是什么?

  • 纹理的二维坐标系 (U,V),范围 0~1,左下角为原点。

  • 作用:告诉 GPU “贴图的哪一部分”贴到“模型的哪个顶点”。

6.2 为什么有多套 UV?

同一模型可能需要不同的贴图映射。例如:

  • 第一套 kzTextureCoordinate0:颜色贴图。

  • 第二套 kzTextureCoordinate1:光照贴图或细节纹理。

在 Studio 中检查网格属性可看到 UV 套数。

6.3 传递多套 UV 示例

// 顶点着色器
attribute vec2 kzTextureCoordinate0;
attribute vec2 kzTextureCoordinate1;
varying vec2 vUV0;
varying vec2 vUV1;

void main() {
    vUV0 = kzTextureCoordinate0;
    vUV1 = kzTextureCoordinate1;
    gl_Position = kzWorldViewProjectionMatrix * vec4(kzPosition, 1.0);
}

// 片段着色器
uniform sampler2D Diffuse;   // 绑到 vUV0
uniform sampler2D Lightmap;  // 绑到 vUV1
varying vec2 vUV0;
varying vec2 vUV1;

void main() {
    vec4 col = texture2D(Diffuse, vUV0);
    vec4 lit = texture2D(Lightmap, vUV1);
    gl_FragColor = col * lit;
}

七、矩阵与坐标空间(白话理解)

7.1 三个核心变换

变换矩阵比喻
WorldkzWorldMatrix把玩具车放到房间某个位置
ViewkzViewMatrix扛起相机对准玩具车
ProjectionkzProjectionMatrix按下快门,拍成2D照片

7.2 最重要的一行代码

gl_Position = kzWorldViewProjectionMatrix * vec4(kzPosition, 1.0);
  • kzPosition 是模型自身的局部坐标 (vec3)。

  • 必须转为 vec4(kzPosition, 1.0)1.0 表示“这是一个点”。

  • 乘法是 左乘:矩阵在左,向量在右。


八、写 Shader 的注意事项清单

  1. 命名:自定义变量一律不用 kz 前缀,避免冲突。

  2. 精度:ES2 片段着色器首行必须写 precision mediump float;,否则编译失败。

  3. 版本#version 必须放第一行。

  4. 纹理采样:ES2 用 texture2D + varying;ES3 用 texture + in/out

  5. 矩阵乘法顺序:永远左乘。

  6. UV 禁止 .uv 成员:UV 是普通 vec2,没有 .uv 这种写法。

  7. 测试:保存即编译,错误时节点变红,看控制台日志。

  8. 性能:尽量用 smoothstep/mix 代替 if 分支。


九、极简代码模板(可以直接复制实验)

9.1 最简顶点着色器

#version 100
attribute vec3 kzPosition;
void main() {
    gl_Position = kzWorldViewProjectionMatrix * vec4(kzPosition, 1.0);
}

9.2 带纹理的片段着色器

#version 100
precision mediump float;
uniform sampler2D MainTexture;
varying vec2 vTexCoord;   // 需顶点着色器传来
void main() {
    gl_FragColor = texture2D(MainTexture, vTexCoord);
}

更多推荐