谢尔宾斯基三角形

500 阅读2分钟

谢尔宾斯基三角形(Sierpinski triangle)是一种分形,由波兰数学家谢尔宾斯基在1915年提出。它是自相似集的例子。它的豪斯多夫维是log(3)/log(2) ≈ 1.585。

确定细分点的算法

算法步骤

  • (1) 随机找一个点p
  • (2) 随机三个顶点之一q
  • (3) 得到p和q的中点,并替换掉p
  • (4) 转至(2)

image.png

代码表示:

// 初始三角形位置
const vertices = [vec2(-1, -1), vec2(0, 1), vec2(1, -1)];

// (1) 随机找一个点p
let p = vec2(Math.random(), Math.random());
const points = [p];

for (var i = 0; i < NumPoints; ++i) {
  // (2) 随机三个顶点之一q
  const q = vertices[Math.floor(Math.random() * 3)];
  // (3) 得到p和q的中点,并替换掉p
  p = scale(0.5, add(points[i], q));
  points.push(p);
  // (4) 转至(2)
}

编写着色器

  • 顶点着色器,确定大小和位置。

packages/sierpinski/src/shaders/vshader21.glsl

attribute vec4 vPosition;


void main() {
    gl_PointSize = 1.0;
    gl_Position = vPosition;
}
  • 片元着色器,确定颜色。

packages/sierpinski/src/shaders/fshader21.glsl

precision mediump float;


void main() {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

初始化着色器

image.png

/**
 * 初始化着色器
 * @param {WebGLRenderingContext} gl WebGL上下文
 * @param {string} vertexShaderId 顶点着色器id
 * @param {string} fragmentShaderId 片元着色器id
 * @returns
 */
const initShaders = (
  gl: WebGLRenderingContext,
  vertexShaderId: string,
  fragmentShaderId: string
) => {
  let vertShdr: WebGLShader | null;
  var fragShdr: WebGLShader | null;


  var vertElem = document.getElementById(vertexShaderId);
  if (!vertElem) {
    console.error(`顶点着色器${vertexShaderId}加载失败`);
    return -1;
  } else {
    // 创建一个着色器对象。 https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/createShader
    vertShdr = gl.createShader(gl.VERTEX_SHADER) as WebGLShader;
    // 设置 WebGLShader 着色器(顶点着色器及片元着色器)。 https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/shaderSource
    gl.shaderSource(vertShdr, vertElem.innerText);
    // 用于编译一个 GLSL 着色器,使其成为为二进制数据。https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/compileShader
    gl.compileShader(vertShdr);
    // 返回给定的着色器信息。https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/getShaderParameter
    if (!gl.getShaderParameter(vertShdr, gl.COMPILE_STATUS)) {
      // 返回指定着色器的编译日志信息。 https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/getShaderInfoLog
      console.error(`着色器编译失败:${gl.getShaderInfoLog(vertShdr)}`);
      return -1;
    }
  }


  var fragElem = document.getElementById(fragmentShaderId);
  if (!fragElem) {
    console.error(`片元着色器${vertexShaderId}加载失败`);
    return -1;
  } else {
    fragShdr = gl.createShader(gl.FRAGMENT_SHADER) as WebGLShader;
    gl.shaderSource(fragShdr, fragElem.innerText);
    gl.compileShader(fragShdr);
    if (!gl.getShaderParameter(fragShdr, gl.COMPILE_STATUS)) {
      console.error(`着色器编译失败:${gl.getShaderInfoLog(vertShdr)}`);
      return -1;
    }
  }


  // 用于创建和初始化一个 WebGLProgram 对象。 https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/createProgram
  var program = gl.createProgram() as WebGLProgram;
  // 往 WebGLProgram 添加一个片段或者顶点着色器。
  gl.attachShader(program, vertShdr);
  gl.attachShader(program, fragShdr);
  // 完成为程序的片元和顶点着色器准备 GPU 代码的过程。 https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/linkProgram
  gl.linkProgram(program);


  // 返回 WebGLProgram 的信息。 https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/getProgramParameter
  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    // 返回参数中指定的WebGLProgram object 的信息。这些信息包括在 linking 过程中的错误以及 WebGLProgram objects 合法性检查的错误。 https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/getProgramInfoLog
    console.error(`链接着色器程序失败:${gl.getProgramInfoLog(program)}`);
    return -1;
  }


  return program;
};


export default initShaders;

WebGL完整绘制流程

image.png

// 配置 WebGL
// 用来设置视口,即指定从标准设备到窗口坐标的 x、y 仿射变换。 https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/viewport
context.viewport(0, 0, canvas.width, canvas.height);
// 设置清空颜色缓冲时的颜色值。 https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/clearColor
context.clearColor(1.0, 1.0, 1.0, 1.0);


// 加载着色器并初始化属性缓冲区。
var program = initShaders(context, 'vertex-shader', 'fragment-shader');
// 指定的WebGLProgram设置为当前呈现状态的一部分。https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/useProgram
context.useProgram(program);


// 将数据加载到GPU
// 创建并初始化存储顶点或颜色等数据的WebGLBuffer。 https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/createBuffer
var bufferId = context.createBuffer();
// 将给定的WebGLBuffer绑定到目标。https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bindBuffer
context.bindBuffer(context.ARRAY_BUFFER, bufferId);
// 初始化并创建缓冲区对象的数据存储。https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bufferData
context.bufferData(context.ARRAY_BUFFER, flatten(points), context.STATIC_DRAW);


// 返回给定WebGLProgram中属性变量的位置。https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/getAttribLocation
var vPos = context.getAttribLocation(program, 'vPosition');
// 将当前绑定到gl.ARRAY_buffer的缓冲区绑定到当前顶点缓冲区对象的通用顶点属性,并指定其布局。 https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/vertexAttribPointer
context.vertexAttribPointer(vPos, 2, context.FLOAT, false, 0, 0);
// 在属性数组列表中的指定索引处打开通用顶点属性数组。https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/enableVertexAttribArray
context.enableVertexAttribArray(vPos);


// 将缓冲区清除为预设值。 https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/clear
context.clear(context.COLOR_BUFFER_BIT);
// 从数组数据渲染图元。 https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/drawArrays
context.drawArrays(context.POINTS, 0, points.length);

绘制效果

参考: