Страницы

суббота, 23 ноября 2013 г.

Mobius strip rendering using WebGL




In this post I want to share experience I accidentally got experimenting with WebGL. To get some fun and explore WebGL (never write a line of JS code before) I render a Mobius strip. It's not the hardest task in the world, but definitely more interesting than usual WebGL getting-started samples with two polygons. And after all, Mobius strip is cool, isn't it?
There is not so much to talk about, we have all the usual stuff such as VBO, shaders and so on. The most interesting part is Mobius strip vertices and polygons calculation. I use parametric representation of Mobius strip for tesselation:
function calcMobiusPoint(u, v, coef) {
    var x = ( 1 + ((v * coef) / 2) * Math.cos(u / 2) ) *
        Math.cos( u );
    var y = ( 1 + ((v * coef) / 2) * Math.cos(u / 2) ) *
        Math.sin( u );
    var z = ( (v * coef) / 2) * Math.sin( u / 2 );
    return [x, y, z];
}

Unfortunately got no time for long explanations; take a look at method that calculates vertices, indexes and colors of strip (below), throw a glance at jsfiddle or download entire sample from bitbucket if necessary.
function initBuffers() {

    // math data for tesselation
    var verticesPerCurve = 120;
    var tBegin = 0.0;
    var tEnd = Math.PI * 2;
    var dt = ( tEnd - tBegin ) / ( verticesPerCurve - 2 );
    var stripCoef = 0.5;

    // surface bounary colors
    var beginColor = [ 0.0, 0.3, 1.0 ];
    var endColor = [ 1.0, 0.3, 0.0 ];

    // vertices calculation scheme:
    //                /           /
    //               /           /
    //          #2  o           o  #(verticesPerCurve + 2)
    //             /           /
    //            /           /
    //       #1  o           o  #(verticesPerCurve + 1)
    //          /           /
    //         /           /
    //    #0  o           o  #(verticesPerCurve + 0)
    //       /           /
    //   curve1      curve2

    // VERTICES
    modelVerticesBuffer = gl.createBuffer();
    gl.bindBuffer( gl.ARRAY_BUFFER, modelVerticesBuffer );

    var vertices = new Array();
    for ( var i = 0; i < verticesPerCurve; ++i ) {
        var t = tBegin + dt * i;
        var p1 = calcMobiusPoint(t, -1.0, stripCoef);
        var p2 = calcMobiusPoint(t, 1.0, stripCoef);
        // first curve vertex
        vertices[ i * 3 ] = p1[ 0 ];
        vertices[ i * 3 + 1 ] = p1[ 1 ];
        vertices[ i * 3 + 2 ] = p1[ 2 ];
        // second curve vertex
        var k = ( i + verticesPerCurve );
        vertices[ k * 3 ] = p2[ 0 ];
        vertices[ k * 3 + 1 ] = p2[ 1 ];
        vertices[ k * 3 + 2 ] = p2[ 2 ];
    }

    gl.bufferData( gl.ARRAY_BUFFER,
        new Float32Array(vertices), gl.STATIC_DRAW );
    modelVerticesBuffer.itemSize = 3;
    modelVerticesBuffer.numItems = verticesPerCurve * 2;

    // COLORS
    modelVerticesColorBuffer = gl.createBuffer();
    gl.bindBuffer( gl.ARRAY_BUFFER,
        modelVerticesColorBuffer );

    var verticesColors = new Array();
    var halfVerticesPerCurve = verticesPerCurve / 2.0;
    for ( var i = 0; i < verticesPerCurve; ++i ) {
        var curColor = [ 0.0, 0.0, 0.0 ];
        for ( var k = 0; k < curColor.length; ++k ) {
            if ( i < halfVerticesPerCurve ) {
                curColor[ k ] = beginColor[ k ] +
                    (endColor[k] - beginColor[k]) *
                    (i / halfVerticesPerCurve);
            } else {
                curColor[ k ] = endColor[ k ] +
                    (beginColor[k] - endColor[k]) *
                    ((i - halfVerticesPerCurve) /
                    halfVerticesPerCurve);
            }
        }
        // first curve vertex color
        verticesColors[ i * 3 ] = curColor[ 0 ];
        verticesColors[ i * 3 + 1 ] = curColor[ 1 ];
        verticesColors[ i * 3 + 2 ] = curColor[ 2 ];
        // second curve vertex color
        var k = ( i + verticesPerCurve );
        verticesColors[ k * 3 ] = curColor[ 0 ];
        verticesColors[ k * 3 + 1 ] = curColor[ 1 ];
        verticesColors[ k * 3 + 2 ] = curColor[ 2 ];
    }

    gl.bufferData( gl.ARRAY_BUFFER,
        new Float32Array(verticesColors), gl.STATIC_DRAW );
    modelVerticesColorBuffer.itemSize = 3;
    modelVerticesColorBuffer.numItems =
        verticesPerCurve * 2;

    // polygons calculation scheme:
    //                 /     \     /
    //                /       \   /
    //               / #poly2  \ /
    //          #2  o-----------o  #(verticesPerCurve + 1)
    //             / \  #poly1 /
    //            /   \       /
    //           /     \     /
    //          /       \   /
    //         / #poly0  \ /
    //    #1  o-----------o  #(verticesPerCurve + 0)
    //       /           /
    //   curve1      curve2

    // POLYGONS
    modelVerticesIndexBuffer = gl.createBuffer();
    gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER,
        modelVerticesIndexBuffer );

    var verticesIndices = new Array();
    for ( var i1 = 0; i1 < (verticesPerCurve - 1); ++i1 ) {
        var i1Next = i1 + 1;
        var i2 = i1 + verticesPerCurve;
        var i2Next = i2 + 1;
        var nPoly1 = i1 * 2;
        var nPoly2 = nPoly1 + 1;
        // first polygon
        verticesIndices[ nPoly1 * 3 ] = i2;
        verticesIndices[ nPoly1 * 3 + 1 ] = i1;
        verticesIndices[ nPoly1 * 3 + 2 ] = i1Next;
        // first polygon
        verticesIndices[ nPoly2 * 3 ] = i1Next;
        verticesIndices[ nPoly2 * 3 + 1 ] = i2Next;
        verticesIndices[ nPoly2 * 3 + 2 ] = i2;
    }

    gl.bufferData( gl.ELEMENT_ARRAY_BUFFER,
        new Uint16Array(verticesIndices), gl.STATIC_DRAW );
    modelVerticesIndexBuffer.itemSize = 3;
    modelVerticesIndexBuffer.numItems =
        verticesIndices.length /
        modelVerticesIndexBuffer.itemSize;
}
Finally, several pictures of rendered Mobius strip:



Комментариев нет: