谢尔宾斯基三角形(Sierpinski triangle)是一种分形,由波兰数学家谢尔宾斯基在1915年提出。它是自相似集的例子。它的豪斯多夫维是log(3)/log(2) ≈ 1.585。
确定细分点的算法
算法步骤
- (1) 随机找一个点p
- (2) 随机三个顶点之一q
- (3) 得到p和q的中点,并替换掉p
- (4) 转至(2)
代码表示:
// 初始三角形位置
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);
}
初始化着色器
/**
* 初始化着色器
* @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完整绘制流程
// 配置 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);
绘制效果
参考: