试图绕过这个。我有以下WebGL代码绘制三角形:
"use strict";
var canvas;
var gl;
var points = [];
window.onload = function init() {
canvas = document.getElementById("gl-canvas");
gl = WebGLUtils.setupWebGL(canvas);
if (!gl) { alert("WebGL isn't available"); }
var vertices = [
vec2(-1, -1),
vec2(0, 1),
vec2(1, -1)
];
// draw a triangle; this is the current output of the program
triangle(vertices[0], vertices[1], vertices[2]);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
var program = initShaders(gl, "vertex-shader", "fragment-shader");
gl.useProgram(program);
var bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, flatten(points), gl.STATIC_DRAW);
var vPosition = gl.getAttribLocation(program, "vPosition");
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
render();
};
function triangle(a, b, c) {
points.push(a, b, c); // (-1, -1), (0, 1), (1, -1)
}
function render() {
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, points.length);
}
从这个[等边]三角形开始,我想把它变成Koch雪花。我有Wikipedia's entry的算法,但我对JavaScript的经验不足使得这有点困难。我知道只需要对上面的代码进行微小的更改,以便将三角形变成Koch雪花。我如何编码所需的算法?
答案 0 :(得分:2)
制作Koch雪花的一种方法是递归制作一堆 用伪代码写的三角形:
/* Return the vertices of a Koch triangle who's left and right sides have
Koch triangles sticking out of them (recursive).
pos: The position of the triangle
dir: The direction triangle should point in
side: The length of one side of the triangle
iterations: The number of triangle babies to make
*/
function kochTriangle(pos, dir, side, iterations):
tri = a big equilateral triangle
if iterations == 1:
return tri
else:
leftTri = recursively create a little kochTriangle on the left side
of tri with iterations - 1
rightTri = recursively create a little kochTriangle on the right
side of tri with iterations - 1
return concat(tri, leftTri, rightTri)
请注意,这只会创建Koch雪花的上半部分,您将拥有
使用此功能创建一个大的Koch三角形朝上和一个
较小的一个坐在底部指向下方(很难解释)。您可以使用
无论是矢量数学还是三角学,都可以确定准确定位的位置
leftTri
和rightTri
。
我虽然这是一个很酷的挑战,所以我真的继续前进并拥有自己的挑战 破解它。我已将我的手机放在下面的可运行的片段中。如果您遇到问题,可以查看下面的代码,但要自己弄清楚它会更有意义。
var SNOWFLAKE_ITERATIONS = 5;
var SNOWFLAKE_SIZE = 1.5;
// How much smaller a triangle's child should be. A traditional Kotch
// snowflake should be 1/3. Change this value to get cool shapes.
var SNOWFLAKE_CHILD_SCALE = 1 / 3;
// Canvas element
var canvas;
// WebGL context
var gl;
// Vertices of the snowflake
var snowflakeVerticies;
// Buffer storing the snowflake's vertices
var snowflakeVertexBuffer;
// Vertex shader attribute
var aPositionAttrib;
function main() {
canvas = document.getElementById("c");
gl = canvas.getContext("webgl");
initSnowflakeVertices();
initShaders();
initBuffers();
drawScene();
}
// Initialize the snowflake's vertices
function initSnowflakeVertices() {
// We have two Koch triangles that make up the snowflake: t1 and t2. t1
// is the top and sides of the snowflake, t2 gives the bottom.
var t1Side = SNOWFLAKE_SIZE;
var t2Side = t1Side * SNOWFLAKE_CHILD_SCALE;
var t1Height = eqTriHeight(t1Side);
var t2Height = eqTriHeight(t2Side);
var snowFlakeHeight = t1Height + t2Height;
var base = vec2(0.0, t2Height - snowFlakeHeight / 2);
var t1Dir = vec2(0.0, 1.0);
var t2Dir = vec2(0.0, -1.0);
var t1 = kochTriangle(base, t1Dir, t1Side, SNOWFLAKE_ITERATIONS);
var t2 = kochTriangle(base, t2Dir, t2Side, SNOWFLAKE_ITERATIONS - 1);
// To clearly see the difference between t1 and t2, you can remove
// the .concat(t2) to hide t2.
snowflakeVerticies = t1.concat(t2);
}
// Initialize the shader program
function initShaders() {
var program = gl.createProgram();
gl.attachShader(program, compileShader("shader-fs"));
gl.attachShader(program, compileShader("shader-vs"));
gl.linkProgram(program);
gl.useProgram(program);
aPositionAttrib = gl.getAttribLocation(program, "aPosition");
gl.enableVertexAttribArray(aPositionAttrib);
}
// Initialize a buffer and put the snowflake's vertices in it
function initBuffers() {
snowflakeVertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, snowflakeVertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(snowflakeVerticies), gl.STATIC_DRAW);
}
function drawScene() {
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, snowflakeVertexBuffer);
gl.vertexAttribPointer(aPositionAttrib, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, snowflakeVerticies.length / 2);
}
// Create the vertices of a Koch triangle who's left and right sides have
// Koch triangles sticking out of them (recursive).
//
// base: The position vector of the base of the triangle
// dir: The unit vector direction triangle should point in
// side: The length of one side of the triangle
// iterations: The number of triangle babies to make
//
// Note that the bottom of the Koch triangle doesn't have another triangle
// sticking out, this is so we don't create unecessary triangles.
function kochTriangle(base, dir, side, iterations) {
// tri is the big triangle that has two little triangles sticking out of
// it's sides.
var tri = eqTri(base, dir, side);
var leftVert = vec2(tri[0], tri[1]);
var rightVert = vec2(tri[2], tri[3]);
var topVert = vec2(tri[4], tri[5]);
if (iterations == 1) {
return tri;
} else {
var leftBase = midpoint(leftVert, topVert);
var leftDir = topVert.minus(leftVert).rotate90DegreesCCW().normalize();
var leftTri = kochTriangle(leftBase, leftDir, side * SNOWFLAKE_CHILD_SCALE, iterations - 1);
var rightBase = midpoint(rightVert, topVert);
var rightDir = topVert.minus(rightVert).rotate90DegreesCW().normalize();
var rightTri = kochTriangle(rightBase, rightDir, side * SNOWFLAKE_CHILD_SCALE, iterations - 1);
return tri.concat(leftTri).concat(rightTri);
}
}
// Create the vertices of an equilateral triangle.
//
// base: The position vector of the base of the triangle
// dir: The unit vector direction triangle should point in
// side: The length of one side of the triangle
function eqTri(base, dir, side) {
var height = eqTriHeight(side);
var leftVert = dir.rotate90DegreesCCW().scale(side / 2).plus(base);
var rightVert = dir.rotate90DegreesCW().scale(side / 2).plus(base);
var topVert = dir.scale(height).plus(base);
return [
leftVert.x, leftVert.y,
rightVert.x, rightVert.y,
topVert.x, topVert.y
];
}
// Get the height of an equilateral triangle with a given side length
function eqTriHeight(side) {
return Math.sqrt(3) / 2 * side;
}
// A minimal 2D vector class. Example usage:
//
// x = vec2(1, 2);
// y = x.rotate90DegreesCW();
// z = x.plus(y);
function vec2(x, y) {
var v = {x: x, y: y};
v.plus = function(w) {
return vec2(v.x + w.x, v.y + w.y);
};
v.minus = function(w) {
return vec2(v.x - w.x, v.y - w.y);
};
v.scale = function(a) {
return vec2(v.x * a, v.y * a);
};
v.rotate90DegreesCW = function() {
return vec2(v.y, -v.x);
};
v.rotate90DegreesCCW = function() {
return vec2(-v.y, v.x);
};
v.normalize = function() {
return v.scale(1 / v.length());
};
v.length = function() {
return Math.sqrt(v.x * v.x + v.y * v.y);
};
return v;
}
// Return the midpoint of two vectors
function midpoint(v, w) {
return v.plus(w).scale(1 / 2);
}
// Compile and return the shader in the given element.
function compileShader(id) {
var script = document.getElementById(id);
if (!script) {
return null;
}
var str = "";
var k = script.firstChild;
while (k) {
if (k.nodeType == 3) {
str += k.textContent;
}
k = k.nextSibling;
}
var shader;
if (script.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (script.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
return null;
}
gl.shaderSource(shader, str);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(id, gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
main();

<script id="shader-vs" type="x-shader/x-vertex">
attribute vec2 aPosition;
void main(void) {
gl_Position = vec4(aPosition, 0.0, 1.0);
}
</script>
<script id="shader-fs" type="x-shader/x-fragment">
void main(void) {
gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
}
</script>
<canvas id="c" width="500" height="500"></canvas>
&#13;