WebGL Planet

Planet

3d рендер на WebGL без фреймворков и библиотек

Oбо мне

my photo
  • Анна Волкова
  • Фронтенд-разработчик 2 года
  • В Альфа-банке в команде AlfaCall

Почему я решила рассказать про WebGL?

  • WebGL - это отличный способ развлечь себя решением нестандартных для фронтендера задач в знакомой среде
  • Возможность узнать что-то новое про компьютерную графику

Что я расскажу?

  • Что такое WebGL?
  • Как WebGL работает?
  • Как рисовать 2d на WebGL
  • Как создать 3d планету на WebGL без использования библиотек и фреймворков

Что такое WebGL?

— это графическое API, которое позволяет отправлять команды на видеокарту из JavaScript

Khronos Group Members

members

Где используется WebGL?

examples

Для работы с растровыми изображениями

filter

Интерактивная презентация машин и интерьеров

3d example

Интерактивная графика
(для проигрывателей музыки)

interactive music

Art + tech

art

Игры

3d игры

Интерактивные примеры

WebGl программы

js
JS программы
+
webgl
Шейдеры на GLSL

Клиент-серверная модель

  • JavaScript code - это клиент
  • Видеокарта - это сервер
  • JavaScript code отправляет запросы на видеокарту через интерфейс WebGL

CPU

vs

GPU

  • Процессор Core i7, 2,7ГГц - 4 ядра, видеокарта Radeon Pro 450 - 640 ядер
  • Видеокарта заточена на параллельное выполнение несвязанных между собой задач
cpu-gpu

Как видеокарта рисует с помощью WebGL?

Описание объекта

— геометрия, состоящая из треугольников и материалов

dolphin

Работа на видеокарте

  • Преобразование позиций треугольников
  • Проекция результирующей модели на экран
  • В растеризаторе спроецированные треугольники разбиваются на фрагменты
  • Определение цвета для каждого фрагмента во фрагментном шейдере
pipelene

Графический pipeline WebGL

pipeline

Input Assembler

(фиксированный этап)

  • С помощью функции gl.bufferData(...) - мы переносим данные из javascript’a на видеркарту
  • Вершинный буффер:
    • Позиция
    • Нормаль
    • Текстурные координаты
          
        const vertices = new Float32Array([
          // vertex 1
         -0.5, -0.5, 0.0,  // position
          1.0, 0.0, 0.0,   // normal
          0.5, 0.5,        // texCoord
          ...
          // vertex n
          1.0, 0.5, 0.0,  // position
          0.0, 1.0, 0.0,  // normal
          1.0, 0.5,       // texCoord
       ]);
          
        

Vertex shader

(програмируемый этап)

Vertex shader

Vertex shader

  • Получаем данные, которые описывают вершину через атрибуты:
    • позиция, нормаль, текстурные координаты
          
      // attribute means it comes from the geometry array we created
      attribute vec3 a_position;
      attribute vec3 a_normal;
      // uniforms - constants
      uniform mat4 u_worldTransform;
      uniform mat4 u_viewProjection;
      // output to fragment shader
      varying vec3 v_normal;

      void main() {
        vec4 worldPos = u_worldTransform * vec4(a_position, 1.0);
        vec4 viewPos = u_viewProjection * worldPos;

        v_normal = mat3(u_worldTransform) * a_normal;
        gl_Position = viewPos;
      }
          
        

Преобразования над данными

  • Матрица трансформации вершин из пространства модели в мировое пространство
  • Матрица трансформации из мирового пространства в пространство камеры
all-steps

Матрица трансформации из пространства камеры в пространство отсечения

clip-space

Проекция

  • Ортогональная - линии проецирования переносятся перпендикулярно плоскости проекции
  • Перспективная - луч проекции идет от проецируемой точки в позицию камеры
projection

gl_Position

  • На выходе вершинный шейдер возвращает позицию вершины в gl_Position
  • gl_Position — значение координат пространства отсечения
ndc

Primitive Assembly

Primitive Assembly

Primitive Assembly

(фиксированный этап)

  • Cобирает из вершин примитивы
  • WebGL - поддерживает три типа примитивов:
    • точки, линии, треугольники
primitive-assembly

Rasterizer

Rasterizer

Rasterizer

(фиксированный этап)

  • Видеокарта определяет, какие пиксели в итоговом Frame buffer перекрываются рисуемым примитивом
  • Интерполяция данных вершин внутри треугольника
rasterization

Fragment Shader

Fragment Shader

Fragment Shader

(програмируемый этап)

  • Для каждого фрагмента, полученного из Rasterizer мы выполняем один Fragment shader
  • На выходе Fragment shader дает цвет итогового фрагмента - gl_FragColor
          
      // uniforms
      uniform vec3 u_lightDir;
      uniform vec3 u_lightColor;
      //input from vertex shader
      varying vec3 v_normal;

      void main() {
        float NoL = max(dot(v_normal, -u_lightDir), 0.0);
        gl_FragColor = vec4(NoL * u_lightColor, 1.0);
      }
          
        

Test and Blending + Output Buffer

Test and Blending

А что если упростить?

pipelene

WebGL 2d

2d-gif

Can i use

can-i-use

WebGL Context

canvas

Quad

quad

Shader Program

          
      // компилируем вершинный шейдер
      const vertexShader = gl.createShader(gl.VERTEX_SHADER);
      gl.shaderSource(vertexShader, vertexShaderCode);
      gl.compileShader(vertexShader); 
  
      // компилируем фрагментный шейдер
      const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
      gl.shaderSource(fragmentShader, fragmentShaderCode);
      gl.compileShader(fragmentShader); 
  
      // линкуем их вместе
      const program = gl.createProgram();
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      gl.linkProgram(program);
          
        

WebGL 2d

2d-gif

WebGL 3D

planet-gif

3 Ways to Create a Mesh for a Sphere

spheres

UV sphere

uv-sphere

ICO sphere

ico-sphere

Normalized Cube

normalized cube

Нормаль

  • Нормаль - это единичный вектор, перпендикулярный поверхности в каждой данной её точке
normal

Перобразование куба в сферу

normalized cube

Генерация карты высот

noise

Функция шума

  • 0.0 - черный, 1.0 - белый
function-noise
          
      // функция noise возвращает случайное число
      float noise (in vec2 st) {
        return fract(sin(dot(st.xy,
          vec2(12.9898,78.233))) * 43758.5453123);
      }
          
        

Приведение шума в соответствие с картой высот

          
    elevation[y][x] = noise(nx, ny);
          
        

3 Октавы шума

noise
          
      elevation[y][x] =  1 * noise(1 * nx, 1 * ny);
      +  0.5 * noise(2 * nx, 2 * ny);
      + 0.25 * noise(4 * nx, 2 * ny);
          
        

Изображение процедурного мира

biome

Планета и облака

planet-gif

AlphaBlend для облаков

            
        gl.enable(gl.BLEND);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

        blendResult = shaderOutput.rgb * shaderOutput.a + // облака
                      frameBuffer.rgb * (1 - shaderOutput.a); // планета
            
        
cloud

Чтобы сделать 3d модель объёмной, ее необходимо осветить

light of earth

Освещение по Фонгу

Phong
  • Фоновое освещение (ambient) — имитация света, достигшего заданной точки после отражения от других объектов.
  • Рассеянное освещение (diffuse) — свет от источника, рассеянный после попадания в заданную точку. В зависимости от угла, под которым падает свет, освещение становится сильнее или слабее.
  • Отраженное освещение (specular) — свет от источника, отраженный после попадания в заданную точку. Отраженный свет виден, если он попадает в камеру.

Освещение

Light
Ip = kα iα + kd (L . N) id + ks (R . V)α  is

Aliasing

fxaa-before fxaa-after

Зубчатый паттерн после растеризации

rasterization

FXAA - fast approximate anti-aliasing

fxaa-before fxaa-after
L = 0.2126 * R + 0.7152 * G + 0.0722 * B

Planet

planet-gif

WebGL libraries

Литература

Спасибо

Planet

planet-gif

Презентация