Web-GL Урок 2

Автор: Евгений Рыжков Дата публикации: 19.03.2013

Описание кода Learning WebGL Lesson 2

<html>
<head>
    <script type="text/javascript" src="js/gl-matrix-2.2.0.min.js"></script>
    <script type="text/javascript" src="js/lesson2.js"></script>

    <script id="shader-fs" type="x-shader/x-fragment">
        /*устанавливается точность плавающей точки*/
        precision mediump float;

        /* принимаем varying переменную vColor содержащую гладко смешанный цвет, полученный линейной интерполяцией */
        varying vec4 vColor;

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

    <script id="shader-vs" type="x-shader/x-vertex">
        /* атрибуты */
        attribute vec3 aVertexPosition; //позиции вершин
        attribute vec4 aVertexColor; // цвета вершин

        /* униформы */
        uniform mat4 uMVMatrix; //матрица трансформации
        uniform mat4 uPMatrix; //матрица перспективы

        /*
         * varying
         * переменная которая передается в пиксельный шейдер
         */
        varying vec4 vColor;

        /* функция вызывается для каждой вершины */
        void main(void) {
            /*записываем позиции вершин*/
            gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);

            /*записываем цвета вершин*/
            vColor = aVertexColor;
        }
    </script>
</head>
<body>
    <canvas id="mycanvas" width="500" height="500"></canvas>
</body>
</html>
window.onload = function(){
    new Lesson2("mycanvas");
};

/*
 *  Lesson2
 *
 *      properties:
 *          canvas (канва),
 *          gl (контекст);
 *
 *      methods:
 *          init (инициализация обьекта),
 *          getShader (получаем код шейдеров),
 *          setMatrixUniforms ( эта функция отправляет в шейдеры матрицу трансформаций и матрицу перспективы ),
 *          initShaders (инициализация шейдеров),
 *          initBuffers (инициализация буферов),
 *          drawScene (отрисовка сцены)
 *
 * */
var Lesson2 = function( id ){
    this.canvas = document.getElementById( id );
    this.gl = this.canvas.getContext("experimental-webgl");


    this.init();
};
    Lesson2.prototype = {

        /* init (инициализация обьекта) */
        init: function(){
            var self = this,
                gl = self.gl;

            self.mvMatrix = mat4.create(); //создаем матрицу model-view [4*4] используя библиотеку glMatrix v0.9.5
            self.pMatrix = mat4.create(); //создаем матрицу projection [4*4] используя библиотеку glMatrix v0.9.5

            self.initShaders();
            self.initBuffers();

            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.enable(gl.DEPTH_TEST);

            self.drawScene();
        },
        /* getShader (получаем код шейдеров) */
        getShader: function (gl, id) {
            var shaderScript = document.getElementById(id);

            if (!shaderScript) {
                return null;
            }

            var str = "",
                k = shaderScript.firstChild;

            while (k) {
                if (k.nodeType == 3) {
                    str += k.textContent;
                }
                k = k.nextSibling;
            }

            var shader;
            if (shaderScript.type == "x-shader/x-fragment") {
                shader = gl.createShader(gl.FRAGMENT_SHADER);
            } else if (shaderScript.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)) {
                alert(gl.getShaderInfoLog(shader));
                return null;
            }

            return shader;
        },

        /* initShaders инициализация шейдеров */
        initShaders: function() {
            var self = this, //ссылка на обьект
                gl = self.gl, //ссылка на контекст
                fragmentShader = self.getShader(gl, "shader-fs"),//получаем пиксельный шейдер
                vertexShader = self.getShader(gl, "shader-vs"); // получаем вертексный шейдер

            /* обьединение шейдеров в программу */
            self.shaderProgram = gl.createProgram();
            gl.attachShader(self.shaderProgram, vertexShader);
            gl.attachShader(self.shaderProgram, fragmentShader);
            gl.linkProgram(self.shaderProgram);

            /*
             * следущие три условия необходимы для отладки в случае ошибки в шейдерах
             *
             * они всегда прописываются после обьеденения шейдеров в программу
             *
             * */
            if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS))
                console.log(gl.getShaderInfoLog(vs));
            if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS))
                console.log(gl.getShaderInfoLog(fs));
            if (!gl.getProgramParameter(self.shaderProgram, gl.LINK_STATUS))
                console.log(gl.getProgramInfoLog(self.shaderProgram));

            /* установка текущей программы */
            gl.useProgram(self.shaderProgram);

            /* получаем ссылку на атрибут определенный в вершинном шейдере */
            self.shaderProgram.vertexPositionAttribute = gl.getAttribLocation(self.shaderProgram, "aVertexPosition");
            /* преобразовуем vertexPositionAttribute в массив */
            gl.enableVertexAttribArray(self.shaderProgram.vertexPositionAttribute);

            /* получаем ссылку на атрибут определенный в вершинном шейдере */
            self.shaderProgram.vertexColorAttribute = gl.getAttribLocation(self.shaderProgram, "aVertexColor");
            /* преобразовуем vertexColorAttribute в массив */
            gl.enableVertexAttribArray(self.shaderProgram.vertexColorAttribute);

            /* получаем ссылки на униформы определенные в вершинном шейдере */
            self.shaderProgram.pMatrixUniform = gl.getUniformLocation(self.shaderProgram, "uPMatrix");
            self.shaderProgram.mvMatrixUniform = gl.getUniformLocation(self.shaderProgram, "uMVMatrix");
        },

        /* setMatrixUniforms эта функция отправляет в шейдеры матрицу трансформаций и матрицу перспективы */
        setMatrixUniforms: function() {
            var self = this,
                gl = self.gl;

            gl.uniformMatrix4fv(self.shaderProgram.pMatrixUniform, false, self.pMatrix);
            gl.uniformMatrix4fv(self.shaderProgram.mvMatrixUniform, false, self.mvMatrix);
        },

        /* initBuffers инициализация буферов */
        initBuffers: function(){
            var self = this, //ссылка на обьект
                gl = self.gl, //ссылка на контекст
                verticesTriangle = [ //вершины треугольника
                    0.0,  1.0,  0.0,
                    -1.0, -1.0,  0.0,
                    1.0, -1.0,  0.0
                ],
                triangleColors = [
                    1.0, 0.0, 0.0, 1.0,
                    0.0, 1.0, 0.0, 1.0,
                    0.0, 0.0, 1.0, 1.0
                ],
                verticesSquare = [ //вершины квадрата
                    1.0,  1.0,  0.0,
                    -1.0,  1.0,  0.0,
                    1.0, -1.0,  0.0,
                    -1.0, -1.0,  0.0
                ],
                squareColors = [];

            /* создание буфера цвета треугольника */
            self.triangleVertexColorBuffer = gl.createBuffer();

            /* установка текущего буфера */
            gl.bindBuffer(gl.ARRAY_BUFFER, self.triangleVertexColorBuffer);

            /* заполнения буфера */
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleColors), gl.STATIC_DRAW);
            self.triangleVertexColorBuffer.itemSize = 4;
            self.triangleVertexColorBuffer.numItems = 3;

            /* создание буфера треугольника */
            self.triangleVertexPositionBuffer = gl.createBuffer();

            /* установка текущего буфера */
            gl.bindBuffer(gl.ARRAY_BUFFER, self.triangleVertexPositionBuffer);

            /* заполняем текущий буфер нашими вершинами */
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verticesTriangle), gl.STATIC_DRAW);

            /* добавляем свои свойства обьекту
             *
             * по умолчанию эти свойства обьекту не нужны.
             * Они будут нам полезны позднее
             *
             * фактически свойства представляют собой три отдельных позиции вершин(numItems),
             * каждая из которых состоит из 3 цифр(itemSize)
             */
            self.triangleVertexPositionBuffer.itemSize = 3;
            self.triangleVertexPositionBuffer.numItems = 3;

            /* создание буфера цвета квадрата */
            self.squareVertexColorBuffer = gl.createBuffer();

            /* установка текущего буфера */
            gl.bindBuffer(gl.ARRAY_BUFFER, self.squareVertexColorBuffer);

            /* масив цветов квадрата */
            for (var i=0; i < 4; i++) {
                squareColors= squareColors.concat([0.5, 0.5, 1.0, 1.0]);
            }

            /* заполнения буфера */
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(squareColors), gl.STATIC_DRAW);
            self.squareVertexColorBuffer.itemSize = 4;
            self.squareVertexColorBuffer.numItems = 4;

            /* создание буфера квадрата */
            self.squareVertexPositionBuffer = gl.createBuffer();

            /* установка текущего буфера */
            gl.bindBuffer(gl.ARRAY_BUFFER, self.squareVertexPositionBuffer);

            /* заполняем текущий буфер нашими вершинами */
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verticesSquare), gl.STATIC_DRAW);
            self.squareVertexPositionBuffer.itemSize = 3;
            self.squareVertexPositionBuffer.numItems = 4;
        },
        /* /initBuffers  */

        /* drawScene отрисовка сцены */
        drawScene: function () {
            var self = this, //ссылка на обьект
                gl = self.gl;//ссылка на контекст

            /* определяем прямоугольную рабочую область на канве
             *
             *   отсчет ведется с нижнего левого угла
             *
             *   gl.viewport(x, y, width, height);
             * */
            gl.viewport(0, 0, self.canvas.width, self.canvas.height);

            /* очистка холста */
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

            /* устанавливаем перспективу */
            mat4.perspective(self.pMatrix, 45, self.canvas.width / self.canvas.height, 0.1, 100.0 );

            /* функция приравнивает model-view(mvMatrix) матрицу к еденичной (устонавливаем центр нашего 3D пространства),
             *
             *  model-view это отпровная точка от которой будут просчитываться все наши преобразования
             *
             */
            mat4.identity(self.mvMatrix);

            /*
             *  нарисуем треугольник
             * */

            /* перемещаем центр нашего 3D пространства в новую точку */
            mat4.translate(self.mvMatrix,self.mvMatrix, vec3.fromValues(-1.5, 0, -7));

            /* устанавливаем текущий буфер */
            gl.bindBuffer(gl.ARRAY_BUFFER, self.triangleVertexPositionBuffer);

            /* отправляем значения текущего буфера в шейдер*/
            gl.vertexAttribPointer(self.shaderProgram.vertexPositionAttribute, self.triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

            /* устанавливаем текущий буфер */
            gl.bindBuffer(gl.ARRAY_BUFFER, self.triangleVertexColorBuffer);

            /* отправляем значения текущего буфера в шейдер*/
            gl.vertexAttribPointer(self.shaderProgram.vertexColorAttribute, self.triangleVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);

            /* говорим WebGL использовать текущую трансформацию */
            self.setMatrixUniforms();

            /* отрисоваваем масив вершин треугольниками(gl.TRIANGLES), начиная с первой вершины(0), и заканчивая последней( self.triangleVertexPositionBuffer.numItems ) */
            gl.drawArrays(gl.TRIANGLES, 0, self.triangleVertexPositionBuffer.numItems);
            /*
             *  /нарисуем треугольник
             *
             * */

            /*
             *  нарисуем квадрат
             * */

            /* перемещаем центр нашего 3D пространства в новую точку */
            mat4.translate(self.mvMatrix, self.mvMatrix, vec3.fromValues(3, 0, 0));

            /* устанавливаем текущий буфер */
            gl.bindBuffer(gl.ARRAY_BUFFER, self.squareVertexPositionBuffer);

            /* отправляем значения текущего буфера в шейдер*/
            gl.vertexAttribPointer(self.shaderProgram.vertexPositionAttribute, self.squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

            /* устанавливаем текущий буфер */
            gl.bindBuffer(gl.ARRAY_BUFFER, self.squareVertexColorBuffer);

            /* отправляем значения текущего буфера в шейдер*/
            gl.vertexAttribPointer(self.shaderProgram.vertexColorAttribute, self.squareVertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);

            /* говорим WebGL использовать текущую трансформацию */
            self.setMatrixUniforms();

            /* отрисоваваем масив вершин полосой треугольников((gl.TRIANGLE_STRIP), начиная с первой вершины(0), и заканчивая последней( self.triangleVertexPositionBuffer.numItems ) */
            gl.drawArrays(gl.TRIANGLE_STRIP, 0, self.squareVertexPositionBuffer.numItems);
        }
        /* /drawScene */
    };

демо-пример