Skip to content

着色器编程入门

一、GLSL简介

GLSL(OpenGL Shading Language)是用于编写着色器的编程语言。它是一种高级编程语言,类似于C语言,但专门用于编写图形硬件(如GPU)上的着色器程序。GLSL是WebGL和OpenGL的核心组成部分,用于实现顶点着色器和片元着色器的功能。

(一)GLSL的基本语法

  1. 变量类型

    • 标量类型float(浮点数)、int(整数)、bool(布尔值)。
    • 向量类型vec2(二维浮点向量)、vec3(三维浮点向量)、vec4(四维浮点向量)。
    • 矩阵类型mat2(2x2矩阵)、mat3(3x3矩阵)、mat4(4x4矩阵)。
    • 结构体类型:可以自定义复杂的数据结构。
    glsl
    float scalar = 1.0;
    vec3 position = vec3(0.0, 0.5, 0.0);
    mat4 transformMatrix = mat4(1.0);
  2. 变量修饰符

    • attribute:用于顶点着色器,表示从顶点数据中接收的输入。
    • varying:用于顶点着色器和片元着色器之间传递数据。
    • uniform:用于从JavaScript代码向着色器传递常量数据。
    glsl
    attribute vec4 a_position; // 顶点位置
    varying vec4 v_color;      // 传递给片元着色器的颜色
    uniform mat4 u_transform;  // 从JavaScript传递的变换矩阵
  3. 函数

    • GLSL支持函数的定义和调用,内置了许多数学函数,如sincosdot(点乘)、cross(叉乘)等。
    glsl
    float calculateLength(vec3 v) {
        return length(v);
    }
  4. 控制语句

    • GLSL支持ifforwhile等控制语句,但通常不建议在着色器中使用复杂的控制逻辑,因为这会影响性能。
    glsl
    if (scalar > 0.5) {
        scalar = 1.0;
    }

二、顶点着色器

(一)顶点着色器的作用

顶点着色器是着色器程序的第一部分,它对每个顶点进行处理。顶点着色器的主要任务包括:

  • 顶点变换:将顶点从模型空间转换到裁剪空间。
  • 光照计算:计算顶点的光照属性(如颜色、法线等)。
  • 传递数据:将处理后的数据(如颜色、纹理坐标等)传递给片元着色器。

(二)如何将顶点数据传递给着色器

顶点数据通过attribute变量传递给顶点着色器。在JavaScript中,需要将顶点数据存储到缓冲区中,并绑定到相应的attribute变量。

javascript
// 创建缓冲区
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

// 将顶点数据存储到缓冲区
const positions = [
    0.0, 0.5, 0.0,
    -0.5, -0.5, 0.0,
    0.5, -0.5, 0.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

// 获取attribute变量的位置
const positionLocation = gl.getAttribLocation(program, 'a_position');

// 绑定缓冲区到attribute变量
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);

顶点着色器代码示例:

glsl
attribute vec4 a_position; // 顶点位置
uniform mat4 u_transform;  // 变换矩阵
varying vec4 v_color;      // 传递给片元着色器的颜色

void main() {
    gl_Position = u_transform * a_position; // 应用变换
    v_color = vec4(1.0, 0.0, 0.0, 1.0);    // 设置颜色
}

三、片元着色器

(一)片元着色器的作用

片元着色器是着色器程序的第二部分,它对每个片元(像素)进行处理。片元着色器的主要任务包括:

  • 颜色计算:计算每个片元的最终颜色。
  • 纹理映射:应用纹理到片元。
  • 光照计算:对片元进行光照计算。

(二)如何计算每个像素的颜色

片元着色器接收顶点着色器传递过来的数据(如颜色、纹理坐标等),并计算最终的像素颜色。片元着色器的输出是gl_FragColor,它决定了屏幕上每个像素的颜色。

片元着色器代码示例:

glsl
precision mediump float; // 精度声明
varying vec4 v_color;     // 从顶点着色器接收的颜色

void main() {
    gl_FragColor = v_color; // 设置片元颜色
}

四、着色器编译和链接

(一)编写着色器代码

着色器代码通常以字符串的形式存储在JavaScript中。顶点着色器和片元着色器分别负责不同的任务,因此需要分别编写。

javascript
const vertexShaderSource = `
    attribute vec4 a_position;
    uniform mat4 u_transform;
    varying vec4 v_color;

    void main() {
        gl_Position = u_transform * a_position;
        v_color = vec4(1.0, 0.0, 0.0, 1.0);
    }
`;

const fragmentShaderSource = `
    precision mediump float;
    varying vec4 v_color;

    void main() {
        gl_FragColor = v_color;
    }
`;

(二)编译着色器

使用WebGL的API将着色器代码编译为着色器对象。

javascript
function createShader(gl, type, source) {
    const shader = gl.createShader(type);
    gl.shaderSource(shader, source);
    gl.compileShader(shader);

    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        console.error('Shader compile failed with: ' + gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
    }

    return shader;
}

const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);

(三)链接着色器到程序

将顶点着色器和片元着色器链接到一个WebGL程序对象中。

javascript
function createProgram(gl, vertexShader, fragmentShader) {
    const program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);

    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        console.error('Program link failed with: ' + gl.getProgramInfoLog(program));
        gl.deleteProgram(program);
        return null;
    }

    return program;
}

const program = createProgram(gl, vertexShader, fragmentShader);
gl.useProgram(program);

(四)传递数据到着色器

通过uniform变量将数据从JavaScript传递到着色器。

javascript
// 获取uniform变量的位置
const transformLocation = gl.getUniformLocation(program, 'u_transform');

// 创建变换矩阵
const transformMatrix = [
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    0, 0, 0, 1
];

// 将变换矩阵传递给着色器
gl.uniformMatrix4fv(transformLocation, false, transformMatrix);

总结

着色器编程是WebGL的核心内容,通过GLSL语言编写顶点着色器和片元着色器,可以实现复杂的图形效果。顶点着色器负责处理顶点数据,片元着色器负责计算每个像素的颜色。通过编译和链接着色器,可以将它们组合成一个WebGL程序,并通过uniform变量传递数据。掌握这些基础知识后,你可以开始探索更高级的图形技术,如纹理映射、光照计算等。

Theme by threelab