Skip to content

WebGL学习笔记 🎮 ​

什么是 WebGL? 🤔 ​

WebGL (Web Graphics Library) 是一种基于 OpenGL ES 的 JavaScript API,用于在网页浏览器中渲染 2D 和 3D 图形。它能够直接调用 GPU 进行图形渲染,提供了强大的图形处理能力。

着色器语言 GLSL ES 基础 ⚡ ​

GLSL ES (OpenGL ES Shading Language) 是 WebGL 中使用的着色器语言。与普通的 JavaScript、C、Java 等在 CPU 上执行的语言不同,GLSL ES 直接在 GPU 上执行,这使得它能够高效地处理图形计算。

基础数据类型 📊 ​

类型说明示例
float浮点型float a = 1.0;
int整型int a = 1;
bool布尔型bool a = true;
vec22维向量vec2 a = vec2(1.0, 0.0);
vec33维向量vec3 a = vec3(1.0, 0.0, 0.0);
vec44维向量vec4 a = vec4(1.0, 0.0, 0.0, 1.0);

常量声明 🔒 ​

glsl
const float PI = 3.14159265359;
const int MAX_POINTS = 100;

函数声明 ⚙️ ​

glsl
// 有返回值的函数
float calculateDistance(vec2 point1, vec2 point2) {
    return distance(point1, point2);
}

// 无返回值的函数
void main() {
    // 着色器入口函数
}

向量操作 📐 ​

glsl
// 三维向量
vec3 position = vec3(1.0, 2.0, 3.0);

// 四维向量(齐次坐标)
vec4 homogeneous = vec4(position, 1.0);

// 向量分量访问
vec4 color = vec4(1.0, 0.0, 0.0, 1.0);
float red = color.r;  // 或 color.x
float alpha = color.a; // 或 color.w

重要的内置变量 🔍 ​

变量名着色器类型说明
gl_Position顶点着色器顶点位置(vec4)
gl_PointSize顶点着色器点的大小(float)
gl_FragColor片元着色器片元颜色(vec4)
gl_FragCoord片元着色器片元坐标(vec4)

WebGL 渲染管线 🔄 ​

基本渲染流程 ​

  1. 顶点着色器处理 (Vertex Processing)
  2. 图元装配 (Primitive Assembly)
  3. 光栅化 (Rasterization)
  4. 片元着色器处理 (Fragment Processing)
  5. 帧缓冲操作 (Framebuffer Operations)

第一个 WebGL 程序示例 💻 ​

javascript
// 获取 WebGL 上下文
const canvas = document.getElementById('webgl');
const gl = canvas.getContext('webgl');

// 顶点着色器
const vertexShaderSource = `
    void main() {
        // 设置点的大小
        gl_PointSize = 20.0;
        // 设置顶点位置
        gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
    }
`;

// 片元着色器
const fragmentShaderSource = `
    void main() {
        // 设置像素颜色为红色
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
`;

// 初始化着色器程序
const program = initShader(gl, vertexShaderSource, fragmentShaderSource);

// 绘制一个点
gl.drawArrays(gl.POINTS, 0, 1);

进阶主题 🚀 ​

1. 纹理映射 ​

  • 图片纹理加载
  • UV 坐标系统
  • 多重纹理

2. 变换矩阵 ​

  • 平移变换
  • 旋转变换
  • 缩放变换
  • 投影矩阵

3. 光照模型 ​

  • 环境光
  • 漫反射
  • 镜面反射
  • Phong 光照模型

性能优化建议 ⚡ ​

  1. 使用 VAO (Vertex Array Object) 管理顶点数据
  2. 合理使用 instancing 绘制重复对象
  3. 避免频繁切换着色器程序
  4. 正确使用 WebGL 状态缓存

调试技巧 🔧 ​

  1. 使用 WebGL Inspector
  2. 检查 shader 编译错误
  3. 使用 performance 工具分析性能
  4. 善用 console 输出调试信息

注意事项 ⚠️ ​

  1. GLSL ES 语句必须以分号结尾
  2. 数据类型要严格匹配
  3. 注意浮点数精度问题
  4. WebGL 坐标系与屏幕坐标系的区别

纹理映射详解 🖼️ ​

纹理基础 ​

javascript
// 创建纹理对象
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);

// 加载图片
const image = new Image();
image.src = 'texture.png';
image.onload = () => {
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
    // 生成多级渐远纹理
    gl.generateMipmap(gl.TEXTURE_2D);
};

UV 坐标系统详解 📐 ​

UV 坐标是二维纹理坐标系统,范围在 [0,1] 之间:

  • U: 水平方向坐标
  • V: 垂直方向坐标
glsl
// 顶点着色器中的纹理坐标
attribute vec2 a_texCoord;
varying vec2 v_texCoord;

void main() {
    v_texCoord = a_texCoord;
    // ...
}

// 片元着色器中的纹理采样
uniform sampler2D u_texture;
varying vec2 v_texCoord;

void main() {
    gl_FragColor = texture2D(u_texture, v_texCoord);
}

多重纹理技术 🎨 ​

javascript
// 激活并绑定多个纹理单元
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture1);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, texture2);

// 在着色器中使用
const fragmentShaderSource = `
    uniform sampler2D u_texture1;
    uniform sampler2D u_texture2;
    varying vec2 v_texCoord;
    
    void main() {
        vec4 color1 = texture2D(u_texture1, v_texCoord);
        vec4 color2 = texture2D(u_texture2, v_texCoord);
        gl_FragColor = mix(color1, color2, 0.5);
    }
`;

变换矩阵详解 🔄 ​

矩阵运算基础 ​

glsl
// 在 GLSL 中定义矩阵
mat4 modelMatrix;
mat4 viewMatrix;
mat4 projectionMatrix;

// 矩阵乘法
vec4 position = projectionMatrix * viewMatrix * modelMatrix * vec4(vertex, 1.0);

常用变换矩阵示例 📊 ​

javascript
// 平移矩阵
function createTranslationMatrix(tx, ty, tz) {
    return new Float32Array([
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        tx, ty, tz, 1
    ]);
}

// 旋转矩阵 (绕 Z 轴)
function createRotationMatrix(angleInRadians) {
    const c = Math.cos(angleInRadians);
    const s = Math.sin(angleInRadians);
    return new Float32Array([
        c, -s, 0, 0,
        s, c, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, 1
    ]);
}

光照模型实现 💡 ​

Phong 光照模型 ​

glsl
// 顶点着色器
attribute vec3 a_position;
attribute vec3 a_normal;
uniform mat4 u_modelMatrix;
uniform mat4 u_viewMatrix;
uniform mat4 u_projectionMatrix;
varying vec3 v_normal;
varying vec3 v_position;

void main() {
    v_normal = normalize(a_normal);
    v_position = vec3(u_modelMatrix * vec4(a_position, 1.0));
    gl_Position = u_projectionMatrix * u_viewMatrix * vec4(v_position, 1.0);
}

// 片元着色器
precision mediump float;
uniform vec3 u_lightPosition;
uniform vec3 u_viewPosition;
uniform vec3 u_lightColor;
varying vec3 v_normal;
varying vec3 v_position;

void main() {
    // 环境光
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * u_lightColor;
    
    // 漫反射
    vec3 norm = normalize(v_normal);
    vec3 lightDir = normalize(u_lightPosition - v_position);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * u_lightColor;
    
    // 镜面反射
    float specularStrength = 0.5;
    vec3 viewDir = normalize(u_viewPosition - v_position);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
    vec3 specular = specularStrength * spec * u_lightColor;
    
    vec3 result = ambient + diffuse + specular;
    gl_FragColor = vec4(result, 1.0);
}

高级技巧与优化 🚀 ​

实例化渲染 (Instancing) ​

javascript
// 启用实例化数组
const instanceMatrix = new Float32Array(instances * 16); // 每个实例一个 4x4 矩阵
const instanceMatrixBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, instanceMatrixBuffer);
gl.bufferData(gl.ARRAY_BUFFER, instanceMatrix, gl.STATIC_DRAW);

// 在顶点着色器中使用
attribute mat4 a_instanceMatrix;
void main() {
    gl_Position = u_projectionMatrix * u_viewMatrix * a_instanceMatrix * vec4(position, 1.0);
}

帧缓冲对象 (FBO) ​

javascript
// 创建帧缓冲
const framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);

// 创建纹理附件
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);

常见问题与解决方案 🔧 ​

1. 纹理上下翻转 ​

javascript
// 在加载纹理前翻转 Y 轴
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);

[待续...]