顶点 · 图元 · Buffer · Attribute · Shader · Color
假设有一个 magic_gpu,请写伪代码:怎样让它在屏幕上画一个红色三角形?
不要写真实WebGL函数,只写“你认为必需的步骤”。
今天的全部内容都挂在这条链上。
位置
颜色
法向量、纹理坐标、温度……
Vertex 是一个带属性的数据记录;三个位置数据本身并不自动等于一个三角形。
6个独立点
(0,1) (2,3) (4,5)
(0,1,2) (3,4,5)
结合 LINE_STRIP、TRIANGLE_STRIP;先画结果,再运行代码验证。
一个顶点定义一个点。
LINES / LINE_STRIP / LINE_LOOP
TRIANGLES / STRIP / FAN
三个点天然确定平面。
内部定义简单。
插值和光栅化容易实现。
GPU围绕三角形高吞吐设计。
连接远距对角线 · 产生细长扁平三角形
连接短距对角线 · 三角形形状更均衡
哪种划分产生的三角形更均衡?为什么“避免扁长三角形”通常更好?
任意内部两点连线必在内部 · 三角化相对简单
存在内部两点连线穿出外部 · 需耳切法等算法
WebGL不负责替你完成一般多边形三角化。
边不自相交,内部/外部定义明确。
任意内部两点连线仍在内部。
所有顶点位于同一平面。
三角形天然满足这些条件,因此最适合作为GPU基本面图元。
固定单顶点引对角线 · $n$ 边凸多边形剖为 $n-2$ 个三角形
检测凸耳逐个切除 · 递归剖分非凸或一般多边形
GPU需要连续、明确类型的数据布局,而不是直接理解任意JavaScript对象。
var p = [x, y];
灵活、动态,可以嵌套对象。
var p = new Float32Array([x, y]);
连续、固定类型,更接近GPU需要的数值内存。
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
将几何顶点数据与全局参数从 JS 传送到 GPU 着色器。
in):位置、法线等每顶点专有数据,通过
Buffer 批量传入。
uniform):变换矩阵、时间等所有顶点共享的全局常量。
顶点计算结果跨阶段传递,在图元内部插值生成片元数据。
out):Vertex Shader
将变换后的颜色或坐标向外输出。
in):光栅化器按重心坐标平滑插值,作为
Fragment 的输入接收。
片元着色器计算出的最终色彩写入显存帧缓冲并呈现上屏。
out):Fragment Shader
经光照与纹理计算,输出片元 RGBA 颜色。
掌握这三个流动方向,就理解了从“JavaScript 数组”到“屏幕彩色像素”的完整着色器数据通信网络。
in 顶点属性 (Buffer)uniform 统一变量out 顶点输出in 片元接收out vec4 片元颜色float / int / bool
vec2 / vec3 / vec4
mat2 / mat3 / mat4
图形学大量操作位置、颜色、方向、变换;GPU硬件也针对这些运算优化。
#version 300 es
in vec4 aPosition;
in vec4 aColor;
loc = gl.getAttribLocation(program,"aPosition");
gl.vertexAttribPointer(loc,2,gl.FLOAT,false,0,0);
gl.enableVertexAttribArray(loc);
out vec4 vColor;
vColor = aColor;
in vec4 vColor;
out vec4 fColor;
fColor = vColor;
三角形内部的片元颜色来自顶点属性的插值。
uTime
uModelView / uProjection
uColor / light params
顶点位置、每顶点颜色、当前时间、变换矩阵、片元最终颜色:分别应该放在哪里?
vec4 a;
a.xy = vec2(1.0, 2.0);
vec3 rgb = a.rgb;
同一向量分量可以用 xyzw / rgba / stpq 三套语义名字访问。
这使颜色、位置、纹理坐标代码更直观。
| 类别 | 示例 | 常见用途 |
|---|---|---|
| 标量 | float / int / bool | 系数、索引、逻辑 |
| 向量 | vec2 / vec3 / vec4 | 位置、方向、颜色、纹理坐标 |
| 矩阵 | mat2 / mat3 / mat4 | 坐标变换 |
| 采样器 | sampler2D | 纹理读取 |
大量Shader invocation并行执行,数据输入/输出由流水线接口明确管理。
向量、矩阵本身就是值类型;Shader通过 in/out/uniform/buffer 等接口访问数据,而不是依赖通用指针操作。
当前Shader阶段的输入。
当前Shader阶段的输出。
一次绘制中共享、由应用程序设置的数据。
WebGL 1.0 中常见 attribute / varying;本课程代码以 WebGL 2 的 in / out 为主。
aPosition:vertex attributevColor:阶段间插值变量uMatrix:uniformfColor:片元输出
vec4 a = vec4(1.0,2.0,3.0,4.0);
vec2 p = a.xy;
vec3 c = a.rgb;
vec4 b = a.yxzw;
gl_Position
先画出你认为的内部颜色分布,再运行程序;最后解释为什么不是Fragment Shader“猜”的。
每个顶点携带不同颜色 → 光栅化插值。
整个对象或一次Draw Call使用同一颜色。
R/G/B 通常使用 0.0–1.0 浮点表达;A 用于透明/混合等效果。
经典真彩色常按每个颜色分量8位存储;现代渲染也常使用更高精度格式。
像素保存调色板索引,而不是完整RGB。
伪彩色可视化、调色板、LUT颜色映射等仍使用类似思想。
顶点颜色在光栅化阶段插值到片元。
整个图元使用统一的离散属性。
in vec3 color;
out vec4 fColor;
void main(){
fColor = color;
}
要求AI“不要重写整段,只解释最小修改及原因”。
为什么顶点不等于点?
为什么GPU需要Buffer?
Vertex Shader 与 Fragment Shader 的区别?