123

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

            
              <!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Sierpinski</title>
  
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec3 vPosition;
attribute vec3 vColor;

uniform mat4 modelView;
uniform mat4 projection;

varying vec4 color;

void main()
{
    gl_Position = projection*modelView*vec4(vPosition, 1.0);
    color = vec4(vColor, 1.0);
}
</script>

<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
   
varying vec4 color;

void main()
{
    gl_FragColor = color;
}
</script>

</head>
<body>
<canvas id="webgl" width="400" height="400" style="margin:0 auto;display:block;"></canvas>
</body>
</html>
            
          
!
            
              /* Reset Default Style */
html, body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var,
b, i,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
  outline: 0;
  font-size: 100%;
  vertical-align: baseline;
}

a {
  margin: 0;
  padding: 0;
  font-size: 100%;
  vertical-align: baseline;
  background: transparent;
  color: inherit;
  outline: none;
  text-decoration: none;
}

html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;
}

canvas{
  margin:0 auto;
  width: 100%;
  height: 100%;
  display:block;
}
            
          
!
            
              /**
 * 跨浏览器RAF
 */
window.requestAnimationFrame = (function () {
  return window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function (callback) {
      window.setTimeout(callback, 1000 / 60);
    };
})();

/**
 * 跨浏览器获取3D绘图上下文
 */
let getWebGLContext = function(canvas, opt) {
  let namesArr = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
  let context = null;
  for (let i = 0; i < namesArr.length; i++) {
    try {
      context = canvas.getContext(namesArr[i], opt);
    } catch(e) {}
    if (context) {
      break;
    }
  }
  return context;
}

/**
 * 编译GLSL ES代码,创建和初始化着色器(顶点和片元)
 * @param {WebGLContext} gl WebGL绘制上下文
 * @param {String} vertexShader 顶点着色器源码
 * @param {String} fragmentShader 片元着色器源码
 * @return program 返回所创建的程序对象
 */
function initShaders(gl, vertexShader, fragmentShader) {
    let vshader;
    vshader = gl.createShader(gl.VERTEX_SHADER);//创建着色器对象
    gl.shaderSource(vshader, vertexShader);//向着色器对象中填充着色器程序的源代码
    gl.compileShader(vshader);//编译着色器
    if (!gl.getShaderParameter(vshader, gl.COMPILE_STATUS)) {//检查着色器的状态
        let msg = "Couldn't compile the vertex shader. The error log is:"
            + "<pre>" + gl.getShaderInfoLog(vshader) + "</pre>";
        //弹出报错信息
        alert(msg);
        gl.deleteShader(vshader);//删除(不是马上删除,会等到程序对象不再使用该着色器后再删除)着色器对象
    return;
        return -1;
    }

    //片元着色器也一样
    let fshader;
    fshader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fshader, fragmentShader);
    gl.compileShader(fshader);
    if (!gl.getShaderParameter(fshader, gl.COMPILE_STATUS)) {
        let msg = "Couldn't compile the fragment shader.  The error log is:"
            + "<pre>" + gl.getShaderInfoLog(fshader) + "</pre>";
        alert(msg);
        gl.deleteShader(fshader);
        return -1;
    }

    //创建程序对象
    let program = gl.createProgram();
    //为程序对象分配着色器
    gl.attachShader(program, vshader);
    gl.attachShader(program, fshader);
    //连接程序对象(在为程序对象分配了两个着色器对象后,还需要将顶点着色器和片元着色器连接起来)
    gl.linkProgram(program);

    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        let msg = "Unable to initialise shaders.  The error log is:"
            + "<pre>" + gl.getProgramInfoLog(program) + "</pre>";
        alert(msg);
        gl.deleteProgram(gl.program);
        gl.deleteProgram(vshader);
        gl.deleteProgram(fshader);
        return -1;
    }

    return program;
}

function setViewPort(canvas, gl) {
    let b = document.body;
    let d = document.documentElement;
    let fullw = Math.max(b.clientWidth, b.scrollWidth, d.scrollWidth, d.clientWidth);
    let fullh = Math.max(b.clientHeight, b.scrollHeight, d.scrollHeight, d.clientHeight);
    canvas.width = fullw;
    canvas.height = fullh;
    gl.clearColor(1.0, 1.0, 1.0, 1.0);
    gl.viewport(0, 0, canvas.width, canvas.height);
}

class LGMV {
    _argumentsToArray(args) {
        return [].concat.apply([], Array.from(args));
    }
    radians( degrees ) {
        return degrees * Math.PI / 180.0;
    }
    //向量初始化
    vec2() {
        let result = this._argumentsToArray(arguments);
        switch (result.length) {
            case 0: result.push(0.0);
            case 1: result.push(0.0);
        }
        return result.splice(0, 2);
    }
    vec3() {
        let result = this._argumentsToArray(arguments);
        switch (result.length) {
            case 0: result.push(0.0);
            case 1: result.push(0.0);
            case 2: result.push(0.0);
        }
        return result.splice(0, 3);
    }
    vec4() {
        let result = this._argumentsToArray(arguments);
        switch (result.length) {
            case 0: result.push(0.0);
            case 1: result.push(0.0);
            case 2: result.push(0.0);
            case 3: result.push(1.0);
        }
        return result.splice(0, 4);
    }
    //矩阵初始化
    mat2() {
        let arg = this._argumentsToArray(arguments);
        let m = [];
        switch (arg.length) {
            case 0:
                arg[0] = 1;
            case 1:
                m = [
                    this.vec2(arg[0], 0.0),
                    this.vec2(0.0, arg[0])
                ];
                break;
            default:
                m.push(this.vec2(arg)); arg.splice(0, 2);
                m.push(this.vec2(arg));
                break;
        }
        m.matrix = true;
        return m;
    }
    mat3() {
        let arg = this._argumentsToArray(arguments);
        let m = [];
        switch (arg.length) {
            case 0:
                arg[0] = 1;
            case 1:
                m = [
                    this.vec3(arg[0], 0.0, 0.0),
                    this.vec3(0.0, arg[0], 0.0),
                    this.vec3(0.0, 0.0, arg[0])
                ];
                break;
            default:
                m.push(this.vec3(arg)); arg.splice(0, 3);
                m.push(this.vec3(arg)); arg.splice(0, 3);
                m.push(this.vec3(arg));
                break;
        }
        m.matrix = true;
        return m;
    }
    mat4() {
        let arg = this._argumentsToArray(arguments);
        let m = [];
        switch (arg.length) {
            case 0:
                arg[0] = 1;
            case 1:
                m = [
                    this.vec4(arg[0], 0.0, 0.0, 0.0),
                    this.vec4(0.0, arg[0], 0.0, 0.0),
                    this.vec4(0.0, 0.0, arg[0], 0.0),
                    this.vec4(0.0, 0.0, 0.0, arg[0])
                ];
                break;

            default:
                m.push(this.vec4(arg)); arg.splice(0, 4);
                m.push(this.vec4(arg)); arg.splice(0, 4);
                m.push(this.vec4(arg)); arg.splice(0, 4);
                m.push(this.vec4(arg));
                break;
        }
        m.matrix = true;
        return m;
    }
    //顶点方法
    //点乘
    dot(u, v) {
        if (u.length != v.length) {
            throw "this.dot(): vectors are not the same dimension";
        }

        let sum = 0.0;
        for (let i = 0; i < u.length; ++i) {
            sum += u[i] * v[i];
        }

        return sum;
    }
    //叉乘
    cross(u, v) {
        if (!Array.isArray(u) || u.length < 3) {
            throw "this.cross(): first argument is not a vector of at least 3";
        }

        if (!Array.isArray(v) || v.length < 3) {
            throw "this.cross(): second argument is not a vector of at least 3";
        }

        let result = [
            u[1] * v[2] - u[2] * v[1],
            u[2] * v[0] - u[0] * v[2],
            u[0] * v[1] - u[1] * v[0]
        ];

        return result;
    }
    negate(u) {
        let result = [];
        for (let i = 0; i < u.length; ++i) {
            result.push(-u[i]);
        }

        return result;
    }
    //矩阵方法
    //相加
    add(u, v) {
        let result = [];

        if (u.matrix && v.matrix) {
            if (u.length != v.length) {
                throw "add(): trying to add matrices of different dimensions";
            }

            for (let i = 0; i < u.length; ++i) {
                if (u[i].length != v[i].length) {
                    throw "add(): trying to add matrices of different dimensions";
                }
                result.push([]);
                for (let j = 0; j < u[i].length; ++j) {
                    result[i].push(u[i][j] + v[i][j]);
                }
            }

            result.matrix = true;

            return result;
        }
        else if (u.matrix && !v.matrix || !u.matrix && v.matrix) {
            throw "add(): trying to add matrix and non-matrix variables";
        }
        else {
            if (u.length != v.length) {
                throw "add(): vectors are not the same dimension";
            }

            for (let i = 0; i < u.length; ++i) {
                result.push(u[i] + v[i]);
            }

            return result;
        }
    }
    //相减
    subtract(u, v) {
        let result = [];

        if (u.matrix && v.matrix) {
            if (u.length != v.length) {
                throw "subtract(): trying to subtract matrices" +
                " of different dimensions";
            }

            for (let i = 0; i < u.length; ++i) {
                if (u[i].length != v[i].length) {
                    throw "subtract(): trying to subtact matrices" +
                    " of different dimensions";
                }
                result.push([]);
                for (let j = 0; j < u[i].length; ++j) {
                    result[i].push(u[i][j] - v[i][j]);
                }
            }

            result.matrix = true;

            return result;
        }
        else if (u.matrix && !v.matrix || !u.matrix && v.matrix) {
            throw "subtact(): trying to subtact  matrix and non-matrix variables";
        }
        else {
            if (u.length != v.length) {
                throw "subtract(): vectors are not the same length";
            }

            for (let i = 0; i < u.length; ++i) {
                result.push(u[i] - v[i]);
            }

            return result;
        }
    }
    //相乘
    mult(u, v) {
        let result = [];

        if (u.matrix && v.matrix) {
            if (u.length != v.length) {
                throw "mult(): trying to add matrices of different dimensions";
            }

            for (let i = 0; i < u.length; ++i) {
                if (u[i].length != v[i].length) {
                    throw "mult(): trying to add matrices of different dimensions";
                }
            }

            for (let i = 0; i < u.length; ++i) {
                result.push([]);

                for (let j = 0; j < v.length; ++j) {
                    let sum = 0.0;
                    for (let k = 0; k < u.length; ++k) {
                        sum += u[i][k] * v[k][j];
                    }
                    result[i].push(sum);
                }
            }

            result.matrix = true;

            return result;
        }

        if (u.matrix && (u.length == v.length)) {
            for (let i = 0; i < v.length; i++) {
                let sum = 0.0;
                for (let j = 0; j < v.length; j++) {
                    sum += u[i][j] * v[j];
                }
                result.push(sum);
            }
            return result;
        }



        else {
            if (u.length != v.length) {
                throw "mult(): vectors are not the same dimension";
            }

            for (let i = 0; i < u.length; ++i) {
                result.push(u[i] * v[i]);
            }

            return result;
        }
    }
    //相等
    equal(u, v) {
        if (u.length != v.length) { return false; }

        if (u.matrix && v.matrix) {
            for (let i = 0; i < u.length; ++i) {
                if (u[i].length != v[i].length) { return false; }
                for (let j = 0; j < u[i].length; ++j) {
                    if (u[i][j] !== v[i][j]) { return false; }
                }
            }
        }
        else if (u.matrix && !v.matrix || !u.matrix && v.matrix) {
            return false;
        }
        else {
            for (let i = 0; i < u.length; ++i) {
                if (u[i] !== v[i]) { return false; }
            }
        }

        return true;
    }
    //归一化
    normalize(u, excludeLastComponent) {
        if (excludeLastComponent) {
            let last = u.pop();
        }

        let len = this.length(u);

        if (!isFinite(len)) {
            throw "normalize: vector " + u + " has zero length";
        }

        for (let i = 0; i < u.length; ++i) {
            u[i] /= len;
        }

        if (excludeLastComponent) {
            u.push(last);
        }

        return u;
    }
    transpose(m) {
        if (!m.matrix) {
            return "transpose(): trying to transpose a non-matrix";
        }

        let result = [];
        for (let i = 0; i < m.length; ++i) {
            result.push([]);
            for (let j = 0; j < m[i].length; ++j) {
                result[i].push(m[j][i]);
            }
        }

        result.matrix = true;

        return result;
    }
    //长度
    length(u) {
        return Math.sqrt(this.dot(u, u));
    }
    //数组类型转换
    flatten(v) {
        if (v.matrix === true) {
            //矩阵先转置(WebGL使用列主序)
            v = this.transpose(v);
        }
        let n = v.length;
        let elemsAreArrays = false;

        if (Array.isArray(v[0])) {
            elemsAreArrays = true;
            n *= v[0].length;
        }

        let floats = new Float32Array(n);

        if (elemsAreArrays) {
            let idx = 0;
            for (let i = 0; i < v.length; ++i) {
                for (let j = 0; j < v[i].length; ++j) {
                    floats[idx++] = v[i][j];
                }
            }
        }
        else {
            for (let i = 0; i < v.length; ++i) {
                floats[i] = v[i];
            }
        }

        return floats;
    }
    mix(u, v, s) {
        if (typeof s !== "number") {
            throw "mix: the last paramter " + s + " must be a number";
        }

        if (u.length != v.length) {
            throw "vector dimension mismatch";
        }

        let result = [];
        for (let i = 0; i < u.length; ++i) {
            result.push((1.0 - s) * u[i] + s * v[i]);
        }

        return result;
    }
    //MVP相关
    lookAt(eye, at, up) {
        if (!Array.isArray(eye) || eye.length != 3) {
            throw "lookAt(): first parameter [eye] must be an a this.vec3";
        }

        if (!Array.isArray(at) || at.length != 3) {
            throw "lookAt(): first parameter [at] must be an a this.vec3";
        }

        if (!Array.isArray(up) || up.length != 3) {
            throw "lookAt(): first parameter [up] must be an a this.vec3";
        }

        if (this.equal(eye, at)) {
            return this.this.mat4();
        }

        let v = this.normalize(this.subtract(at, eye));  // view direction vector
        let n = this.normalize(this.cross(v, up));       // perpendicular vector
        let u = this.normalize(this.cross(n, v));        // "new" up vector

        v = this.negate(v);

        let result = this.mat4(
            this.vec4(n, -this.dot(n, eye)),
            this.vec4(u, -this.dot(u, eye)),
            this.vec4(v, -this.dot(v, eye)),
            this.vec4()
        );
        return result;
    }

    //平行投影
    ortho(left, right, bottom, top, near, far) {
        //视见体是对称的
        //坐标映射:x => [-1.1] y => [-1,1] z => [0,1]
        //矩阵表示(通用): 
        //r => right l => left t => top b => bottom f => far n => near 
        //[ 2/(r-l), 0, 0, -(r+l)/(r-l)
        //  0, 2/(t-b), 0, -(t+b)/(t-b)
        //  0, 0, -2/(f-n), -(f+n)/(f-n)
        //  0, 0, 0, 1]
        // 对称时:r = -l, t = -b ....
        // 可简化成:
        //[ 1/r, 0, 0, 0
        //  0, 1/t, 0, 0
        //  0, 0, -2/(f-n), -(f+n)/(f-n)
        //  0, 0, 0, 1]
         if (left == right) { throw "ortho(): left and right are equal"; }
        if (bottom == top) { throw "ortho(): bottom and top are equal"; }
        if (near == far) { throw "ortho(): near and far are equal"; }

        let w = right - left;
        let h = top - bottom;
        let d = far - near;

        let result = this.mat4();
        result[0][0] = 2.0 / w;
        result[1][1] = 2.0 / h;
        result[2][2] = -2.0 / d;
        result[0][3] = -(left + right) / w;
        result[1][3] = -(top + bottom) / h;
        result[2][3] = -(near + far) / d;

        return result;
    }

    //透视投影
    perspective(fovy, aspect, near, far) {
        //aspect = width /height => 2r/2t(对称的情况下) => r/t
        //f = 1/ (top/near)  视角还少一半 => n/t
        var f = 1.0 / Math.tan(this.radians(fovy) / 2);
        var d = far - near;
        //和平息投影原理时一样的,后面的推导加个相似三角形
        //通用:
        //[ 2n/(r-l), 0, (r+l)/(r-l), 0
        //  0, 2n/(t-b), (t+b)/(t-b), 0
        //  0, 0, -(f+n)/(f-n), -2fn/(f-n)
        //  0, 0, -1, 0]
        //对称简化后:
        //[ n/r, 0, 0, 0
        //  0, n/t, 0, 0
        //  0, 0, -(f+n)/(f-n), -2fn/(f-n)
        //  0, 0, -1, 0 ]
        var result = this. mat4();
        result[0][0] = f / aspect;
        result[1][1] = f;
        result[2][2] = -(near + far) / d;//这里的符号问题
        result[2][3] = -2 * near * far / d;//这里的符号问题
        result[3][2] = -1;
        result[3][3] = 0.0;//默认是1,手动置0

        return result;
    }
}

//起手式
let canvas = document.getElementById("webgl");
let gl = getWebGLContext(canvas);
setViewPort(canvas, gl);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.enable(gl.DEPTH_TEST);

let MV = new LGMV();

//顶点
let vertices = [
    //单位圆上四点
    MV.vec3(0.0000, 0.0000, -1.0000),
    MV.vec3(0.0000, 0.9428, 0.3333),
    MV.vec3(-0.8165, -0.4714, 0.3333),
    MV.vec3(0.8165, -0.4714, 0.3333)
];

//起始点
let points = [];
let colors = [];
let NumTimesToSubdivide = 3;//分型次数
//分型
divideTetra(vertices[0], vertices[1], vertices[2], vertices[3],
    NumTimesToSubdivide);

let vshader = document.getElementById("vertex-shader").text;
let fshader = document.getElementById("fragment-shader").text;
let program = initShaders(gl, vshader, fshader);
gl.useProgram(program);

let cBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cBuffer);
gl.bufferData(gl.ARRAY_BUFFER, MV.flatten(colors), gl.STATIC_DRAW);

let vColor = gl.getAttribLocation(program, "vColor");
gl.vertexAttribPointer(vColor, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vColor);

let vBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer);
gl.bufferData(gl.ARRAY_BUFFER, MV.flatten(points), gl.STATIC_DRAW);

let vPosition = gl.getAttribLocation(program, "vPosition");
gl.vertexAttribPointer(vPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);

let modelView = gl.getUniformLocation(program, "modelView");
let projection = gl.getUniformLocation(program, "projection");

//MVP矩阵所需变量
let MVMatrix, pMatrix;//MVP!!!
let eye;

let radius = 1.0;
let phi = 0.0;
let dr = 1.0 * Math.PI / 180.0;//1度(角度转弧度便于Math.sin\cos)

const at = MV.vec3(0.0, 0.0, 0.0);//look at point
const up = MV.vec3(0.0, 1.0, 0.0);//视点上方向

let fovy = 45.0;
let aspect =  canvas.width/canvas.height;
let near = 0.3;
let far = 4.0;
//平行投影
pMatrix = MV.ortho(-1.0, 1.0, -1.0, 1.0, 0.0, 4.0);//left, right, bottom, top, near, far
//透视投影
//pMatrix = MV.perspective(fovy, aspect, near, far);
gl.uniformMatrix4fv(projection, false, MV.flatten(pMatrix));

//render
function render() {
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    phi += dr;
    //变换视点的位置(x、y、z)
    eye = MV.vec3(
        radius * Math.sin(phi), 
        1, 
        radius * Math.cos(phi));

    MVMatrix = MV.lookAt(eye, at, up);

    gl.uniformMatrix4fv(modelView, false, MV.flatten(MVMatrix));
    gl.drawArrays(gl.TRIANGLES, 0, points.length);

    window.requestAnimationFrame(render);
}

render();

//分型方法
function divideTetra(a, b, c, d, count) {
    if (count === 0) {
        tetra(a, b, c, d);
    }

    else {
        //6个中点
        var ab = MV.mix(a, b, 0.5);
        var ac = MV.mix(a, c, 0.5);
        var ad = MV.mix(a, d, 0.5);
        var bc = MV.mix(b, c, 0.5);
        var bd = MV.mix(b, d, 0.5);
        var cd = MV.mix(c, d, 0.5);

        --count;
        //4个小三角体
        divideTetra(a, ab, ac, ad, count);
        divideTetra(ab, b, bc, bd, count);
        divideTetra(ac, bc, c, cd, count);
        divideTetra(ad, bd, cd, d, count);
        //继续迭代
    }
}

function tetra(a, b, c, d) {
    triangle(a, c, b);
    triangle(a, c, d);
    triangle(a, b, d);
    triangle(b, c, d);
}


function triangle(a, b, c) {
    //顶点随机取色
    colors.push(MV.vec3(Math.random(), Math.random(), Math.random()));
    points.push(a);
    colors.push(MV.vec3(Math.random(), Math.random(), Math.random()));
    points.push(b);
    colors.push(MV.vec3(Math.random(), Math.random(), Math.random()));
    points.push(c);
}


            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.

Console