Уроки, алгоритмы, программы, примеры

Материалы по разделам

Построения
на плоскости (2D)
Графика
в пространстве (3D)
Вычислительная
геометрия
Физическое
моделирование
Фрактальная
графика

Новые комментарии

У меня проблема вот с этим: gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);. Вылезает ошибка: CS1061 "object" не содержит определения "GL_COLOR_BUFFER_BIT", и не удалось найти доступный метод расширения "GL_COLOR_BUFFER_BIT",...
Большое спасибо. Единственный код который прошел без каких либо ошибок. Ура!!!
Скажите пожалуйста, подскажите алгоритм по которому по заданным точкам можно определить тип многогранника, скажем это куб или прямоугольный параллелепипед. Нашел теорию по этим фигурам: https://www.mat... https://www.mat... Акцентировать внимание...
Всем у кого не работает. файл wizard.script Ещё одно упоминание Glut32 в строке "if (!VerifyLibFile(dir_nomacro_lib, _T("glut32"), _T("GLUT's"))) return false;" меняем на "if (!VerifyLibFile(dir_nomacro_lib, _T("freeglut"), _T("GLUT's"))) return...
Не получается, емаё

Счетчики и рейтинг

Рейтинг@Mail.ru Яндекс.Метрика
Среда программирования: 
HTML 5 + JavaScript


В этом уроке мы рассмотрим освещение вращающегося куба, сделанное с помощью WebGL.
Мы будем использовать простой направленный и окружающий свет, для этого нам нужно изучить данные пункты в следующем порядке:

  1. Мы должны привязать нормаль поверхности к каждой вершине. Это вектор, который направлен перпендикулярно грани в данной вершине.
  2. Нам нужно знать направление, в котором распространяется свет. Оно определяется вектором направления.

Также нам потребуется обновить вершинный шейдер, чтобы скорректировать цвет каждой вершины в зависимости от окружающего и направленного освещения с учётом угла падения на грань.

Построение нормали для вершин

Для начала нам необходимо создать массив нормалей для всех вершин, из которых состоит наш куб.

const normalBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
 
  const vertexNormals = [
    // Передняя грань
     0.0,  0.0,  1.0,
     0.0,  0.0,  1.0,
     0.0,  0.0,  1.0,
     0.0,  0.0,  1.0,
 
    // Задняя грань
     0.0,  0.0, -1.0,
     0.0,  0.0, -1.0,
     0.0,  0.0, -1.0,
     0.0,  0.0, -1.0,
 
    // Верхняя грань
     0.0,  1.0,  0.0,
     0.0,  1.0,  0.0,
     0.0,  1.0,  0.0,
     0.0,  1.0,  0.0,
 
    // Нижняя грань
     0.0, -1.0,  0.0,
     0.0, -1.0,  0.0,
     0.0, -1.0,  0.0,
     0.0, -1.0,  0.0,
 
    // Правая грань
     1.0,  0.0,  0.0,
     1.0,  0.0,  0.0,
     1.0,  0.0,  0.0,
     1.0,  0.0,  0.0,
 
    // Левая грань
    -1.0,  0.0,  0.0,
    -1.0,  0.0,  0.0,
    -1.0,  0.0,  0.0,
    -1.0,  0.0,  0.0
  ];
 
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexNormals),
                gl.STATIC_DRAW);
 
...
 
  return {
    position: positionBuffer,
    normal: normalBuffer,
    textureCoord: textureCoordBuffer,
    indices: indexBuffer,
  };

Мы создаём новый буфер, связываем его с буфером, в котором хранятся вершины нашего куба, и записываем в него массив нормалей при помощи bufferData().

Затем добавим в drawScene() код, который свяжет массив нормалей с атрибутом шейдера. Это делается для того, чтобы шейдер смог получить к нему доступ.

// Указываем WebGL как извлекать нормали из
  // буфера нормалей в атрибут vertexNormal.
  {
    const numComponents = 3;
    const type = gl.FLOAT;
    const normalize = false;
    const stride = 0;
    const offset = 0;
    gl.bindBuffer(gl.ARRAY_BUFFER, buffers.normal);
    gl.vertexAttribPointer(
        programInfo.attribLocations.vertexNormal,
        numComponents,
        type,
        normalize,
        stride,
        offset);
    gl.enableVertexAttribArray(
        programInfo.attribLocations.vertexNormal);
  }

В конце нужно обновить код, который строит матрицы для uniform-переменных, чтобы создать и передать в шейдер матрицу нормалей, которая используется для трансформации нормалей при расчёте ориентации куба относительно направления на источник света:

const normalMatrix = mat4.create();
  mat4.invert(normalMatrix, modelViewMatrix);
  mat4.transpose(normalMatrix, normalMatrix);
 
...
 
  gl.uniformMatrix4fv(
      programInfo.uniformLocations.normalMatrix,
      false,
      normalMatrix);

Обновление шейдеров

Мы получили все необходимые данные, поэтому не забываем, что пора обновить код шейдеров.

Вершинный шейдер

Вершинный шейдер обновляем таким образом, чтобы он, на основе направленного и окружающего света, рассчитывал освещение на каждой вершине куба.

const vsSource = `
    attribute vec4 aVertexPosition;
    attribute vec3 aVertexNormal;
    attribute vec2 aTextureCoord;
 
    uniform mat4 uNormalMatrix;
    uniform mat4 uModelViewMatrix;
    uniform mat4 uProjectionMatrix;
 
    varying highp vec2 vTextureCoord;
    varying highp vec3 vLighting;
 
    void main(void) {
      gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
      vTextureCoord = aTextureCoord;
 
      // Применяем эффект освещения
 
      highp vec3 ambientLight = vec3(0.3, 0.3, 0.3);
      highp vec3 directionalLightColor = vec3(1, 1, 1);
      highp vec3 directionalVector = normalize(vec3(0.85, 0.8, 0.75));
 
      highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);
 
      highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);
      vLighting = ambientLight + (directionalLightColor * directional);
    }
  `;

Мы рассчитываем позицию вершины, передаём её во фрагментный шейдер, после этого можем произвести расчёт освещения вершины. Для начала преобразовываем нормаль: умножаем нормаль вершины на матрицу нормалей. Вычисляем количество света от направленного источника и в конце мы можем посчитать полное освещение, сложив окружающий свет и произведение количества направленного света на его цвет.

Фрагментный шейдер

В фрагментном шейдере всё проще: необходимо, чтобы он учитывал значение освещения, полученное в вершинном шейдере.

const fsSource = `
    varying highp vec2 vTextureCoord;
    varying highp vec3 vLighting;
 
    uniform sampler2D uSampler;
 
    void main(void) {
      highp vec4 texelColor = texture2D(uSampler, vTextureCoord);
 
      gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a);
    }
  `;

Мы получаем цвет текселя и умножаем на значение освещения, чтобы учесть влияние источников света.

Осталось только посмотреть на определение атрибута aVertexNormal и uniform-переменной uNormalMatrix.

 const programInfo = {
    program: shaderProgram,
    attribLocations: {
      vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
      vertexNormal: gl.getAttribLocation(shaderProgram, 'aVertexNormal'),
      textureCoord: gl.getAttribLocation(shaderProgram, 'aTextureCoord'),
    },
    uniformLocations: {
      projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
      modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
      normalMatrix: gl.getUniformLocation(shaderProgram, 'uNormalMatrix'),
      uSampler: gl.getUniformLocation(shaderProgram, 'uSampler'),
    },
  };

Ниже вы можете увидеть результат выполнения программы:

Прикрепленный файлРазмер
lighting_in_webgl_Nosalik.rar5.86 кб