首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >利用WebGL将三角形转化为科赫雪花

利用WebGL将三角形转化为科赫雪花
EN

Stack Overflow用户
提问于 2016-01-27 04:05:41
回答 1查看 1.9K关注 0票数 3

想把我的头绕在这上面。我有以下WebGL代码,它绘制一个三角形:

代码语言:javascript
复制
"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);
}

从这个等边三角形开始,我想把它变成一个科赫雪花。多亏了维基百科条目,我才有了算法,但是我对JavaScript缺乏经验,这使得这有点困难。据我所知,只需要对上面的代码做一些小小的修改,就可以将三角形变成Koch雪花。我将如何编码所需的算法?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-01-27 13:38:39

制造Koch雪花的一种方法是递归地生成一组三角形,用伪代码编写:

代码语言:javascript
复制
/* 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三角形,并将一个较小的三角形定位在下面(难以解释)。您可以使用向量数学或三角函数来确定定位leftTrirightTri的确切位置。

我认为这是一个很酷的挑战,所以我确实坚持了下来,并对此有了自己的见解。我已经把我的放在下面的片段里了。如果您被困在下面的代码中,您可以查看下面的代码,但是自己找出更有意义。

JSFiddle

代码语言:javascript
复制
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();
代码语言:javascript
复制
<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>

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35028693

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档