返回

掌握GLSL:绘制多个2D形状并发挥创作混搭

前端

在WebGL的广阔天地中,2D图形始终扮演着不可或缺的角色,今天我们继续探索GLSL在绘制2D图形中的神奇力量,重点关注如何绘制多个2D形状,以及如何让这些形状在画布上和谐共舞。让我们从一个简单的圆形开始,逐步深入到更复杂的图形世界。

绘就多彩圆形,点缀视觉画布

// 定义圆形着色器代码
const vertexShaderSource = `
  attribute vec2 a_position;
  uniform vec2 u_resolution;

  void main() {
    // 将顶点位置归一化到[-1, 1]范围内
    vec2 normalizedPosition = a_position / u_resolution;

    // 计算圆形的半径
    float radius = 0.5;

    // 检查顶点是否在圆内
    if (distance(normalizedPosition, vec2(0.0, 0.0)) < radius) {
      // 如果顶点在圆内,则将片段颜色设置为红色
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    } else {
      // 如果顶点不在圆内,则将片段颜色设置为黑色
      gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
    }
  }
`;

// 定义片段着色器代码
const fragmentShaderSource = `
  precision mediump float;

  void main() {
    // 输出片段颜色
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  }
`;

// 创建着色器程序
const shaderProgram = createShaderProgram(vertexShaderSource, fragmentShaderSource);

// 从着色器程序中获取顶点位置属性和分辨率变量的位置
const positionAttributeLocation = gl.getAttribLocation(shaderProgram, "a_position");
const resolutionUniformLocation = gl.getUniformLocation(shaderProgram, "u_resolution");

// 创建一个缓冲区来存储圆形的顶点数据
const positionBuffer = gl.createBuffer();

// 将顶点数据绑定到缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

// 设置缓冲区数据
const positions = [
  -1.0, -1.0,
  1.0, -1.0,
  -1.0, 1.0,
  1.0, 1.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

// 启用顶点位置属性并指定其数据格式
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

// 设置分辨率变量
gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);

// 清除画布
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

// 使用着色器程序
gl.useProgram(shaderProgram);

// 绘制圆形
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

上述代码以一个红色的圆形作为开场,这只是我们旅程的开始。接下来,我们将把圆形转化为一个可以随意移动的小精灵,并添加更多的形状,让它们在画布上翩翩起舞。

画笔挥洒,描绘缤纷世界

// 定义精灵着色器代码
const vertexShaderSource = `
  attribute vec2 a_position;
  uniform vec2 u_resolution;
  uniform vec2 u_translation;

  void main() {
    // 将顶点位置归一化到[-1, 1]范围内
    vec2 normalizedPosition = a_position / u_resolution;

    // 将顶点位置加上平移量
    vec2 translatedPosition = normalizedPosition + u_translation;

    // 计算圆形的半径
    float radius = 0.5;

    // 检查顶点是否在圆内
    if (distance(translatedPosition, vec2(0.0, 0.0)) < radius) {
      // 如果顶点在圆内,则将片段颜色设置为红色
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    } else {
      // 如果顶点不在圆内,则将片段颜色设置为黑色
      gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
    }
  }
`;

// 定义片段着色器代码
const fragmentShaderSource = `
  precision mediump float;

  void main() {
    // 输出片段颜色
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  }
`;

// 创建着色器程序
const shaderProgram = createShaderProgram(vertexShaderSource, fragmentShaderSource);

// 从着色器程序中获取顶点位置属性和分辨率变量的位置
const positionAttributeLocation = gl.getAttribLocation(shaderProgram, "a_position");
const resolutionUniformLocation = gl.getUniformLocation(shaderProgram, "u_resolution");
const translationUniformLocation = gl.getUniformLocation(shaderProgram, "u_translation");

// 创建一个缓冲区来存储精灵的顶点数据
const positionBuffer = gl.createBuffer();

// 将顶点数据绑定到缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

// 设置缓冲区数据
const positions = [
  -1.0, -1.0,
  1.0, -1.0,
  -1.0, 1.0,
  1.0, 1.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

// 启用顶点位置属性并指定其数据格式
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);

// 设置分辨率变量
gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);

// 设置平移变量
gl.uniform2f(translationUniformLocation, 0.0, 0.0);

// 清除画布
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

// 使用着色器程序
gl.useProgram(shaderProgram);

// 绘制精灵
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

在这个代码中,精灵可以随心所欲地移动,为我们的画布增添了一份灵动和活力。为了丰富画布的内容,我们还可以添加更多形状,例如一个蓝色的正方形,并让它们一起在画布上共舞。

色彩交融,碰撞出创意火花

// 定义精灵着色器代码
const vertexShaderSource = `
  attribute vec2 a_position;
  uniform vec2 u_resolution;
  uniform vec2 u_translation;

  void main() {
    // 将顶点位置归一化到[-1, 1]范围内
    vec2 normalizedPosition = a_position / u_resolution;

    // 将顶点位置加上平移量
    vec2 translatedPosition = normalizedPosition + u_translation;

    // 计算圆形的半径
    float radius = 0.5;

    // 检查顶点是否在圆内
    if (distance(translatedPosition, vec2(0.0, 0.0)) < radius) {
      // 如果顶点在圆内,则将片段颜色设置为红色
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    } else {
      // 如果顶点不在圆内,则将片段颜色设置为黑色
      gl_FragColor = vec4(0.0, 0.0, 0.