WebGL. Введение.

Мстислав Живодков

WebGL

  1. Немного урезанный вариант OpenGL ES 2.0
  2. Спецификация выпущена в 2011 году
  3. Поддерживается Kronos Group
  4. Для работы использует контекст canvas

Работает у 81% пользователей

WebGL

  1. Рисует 3D? На самом деле нет

Шейдеры

  1. Программа
  2. Выполняются на видеокарте
  3. Используют язык GLSL
  4. Есть два вида шейдеров: вершинный и фрагментный

Vertex Shader

Преобразовывает координаты вершин

Fragment Shader

Преобразовывает цвет пикселей

Рисуем куб

Инициализация WebGL

var canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);

var gl = canvas.getContext('webgl');
gl.viewport(0, 0, window.innerWidth,
  window.innerHeight);

Рисуем куб

Вершины

            [ [-1, -1], [1, -1], [-1, 1] ]
            [ [1, 1], [-1, 1], [1, -1] ]
        
			->
            [ [-1, -1, -1], [1, -1, -1],
  [-1, 1, -1] ]
            [ [1, 1, -1], [-1, 1, -1],
  [1, -1, -1] ]
		
            [-1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1]
		

Больше вершин


[ -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1,
 -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, -1, -1, 1, 1,
 -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1,
 -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1,
 -1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1,
 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, 1, 1, -1 ]
        

Буфер вершин

var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER,
    new Float32Array(vertices), gl.STATIC_DRAW);

Цвет

            [ [255, 128, 128], [255, 128, 128],
[255, 128, 128] ]
            [ [255, 128, 128], [255, 128, 128],
[255, 128, 128] ]
        

Больше цвета


[ 255, 128, 128, 255, 128, 128, 255, 128, 128, 255, 128, 128, 255, 128, 128, 255, 128, 128,
 255, 128, 128, 255, 128, 128, 255, 128, 128, 255, 128, 128, 255, 128, 128, 255, 128, 128,
 128, 179, 255, 128, 179, 255, 128, 179, 255, 128, 179, 255, 128, 179, 255, 128, 179, 255,
 128, 179, 255, 128, 179, 255, 128, 179, 255, 128, 179, 255, 128, 179, 255, 128, 179, 255,
 85, 255, 85, 85, 255, 85, 85, 255, 85, 85, 255, 85, 85, 255, 85, 85, 255, 85,
 85, 255, 85, 85, 255, 85, 85, 255, 85, 85, 255, 85, 85, 255, 85, 85, 255, 85 ]
        
colors = colors.map(function(t) { return t / 255; });

Буфер цветов

var colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.bufferData(gl.ARRAY_BUFFER,
    new Float32Array(colors), gl.STATIC_DRAW);

Матрица перспективы

var pMatrix = mat4.create();
mat4.perspective(pMatrix, 45,
 window.innerWidth / window.innerHeight,
 0.1, 1000);
mat4.translate(pMatrix, pMatrix,
 [0, 0, -5]);

Vertex Shader

attribute vec3 position;
attribute vec3 color;

uniform mat4 pMatrix;
uniform mat4 mvMatrix;

varying vec3 v_color;

void main(void) {
  v_color = color;
  gl_Position = pMatrix * mvMatrix * vec4(position, 1.0);
}

Fragment Shader

precision mediump float;

varying vec3 v_color;

void main(void) {
  gl_FragColor = vec4(v_color.rgb, 1.0);
}

Инициализация шейдеров

var vertexShader = gl.createShader(gl.VERTEX_SHADER);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);

gl.shaderSource(vertexShader,
  document.getElementById('vertexShader').text);
gl.compileShader(vertexShader);

gl.shaderSource(fragmentShader,
  document.getElementById('fragmentShader').text);
gl.compileShader(fragmentShader);

Инициализация программы

var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);

var pMatrixUniform = gl.getUniformLocation(program, 'pMatrix');
var mvMatrixUniform = gl.getUniformLocation(program, 'mvMatrix');
var positionAttribution = gl.getAttribLocation(program, 'position');
var colorAttribution = gl.getAttribLocation(program, 'color');

Render loop

requestAnimationFrame(render);

mat4.rotateY(mvMatrix, mvMatrix, dt / 1000);
mat4.rotateZ(mvMatrix, mvMatrix, dt / 1000);

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

gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

gl.useProgram(program);

Render loop

gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.enableVertexAttribArray(positionAttribution);
gl.vertexAttribPointer(positionAttribution, 3, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.enableVertexAttribArray(colorAttribution);
gl.vertexAttribPointer(colorAttribution, 3, gl.FLOAT, false, 0, 0);

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

gl.drawArrays(gl.TRIANGLES, 0, 36);

Полезные ссылки

GPU vs CPU

  1. Серия уроков WebGL learning
  2. Доклад Агафонкина о mapbox-gl
  3. Работа с матрицами gl-matrix
  1. Весь код с презентации
  2. trufi.github.io/webgl-introduction

Fork me on Github