30
loading...
This website collects cookies to deliver better user experience
Я пишу статью, спустя 2 месяца после того как закончил работу над задачей и осталась только такая песочница ->
The triangulate function splits an ngon into individual triangles. This commit fixes a bug where the mesh parsing code was ignoring the emitted triangles and instead using the original ngon indices.
attribute vec4 a_position; // объявляем переменную в которую будем прокидывать вершины яблока.
uniform mat4 u_matrix; // матрица которая будет нам помогать трансформировать модель
void main(){
gl_Position = u_matrix * a_position; // у glsl есть встроенные возможности по работе с матрицами. Тут он сам за нас перемножает вершины на матрицы и тем самым смещает их куда надо.
}
precision mediump float; // точность для округления.
void main() {
gl_FragColor = vec4(1., 0., 0., 1.); // заливаем красным
}
import { vertex, fragment } from './shaders'; // через parcel импортирует тексты
import { createCanvas, createProgramFromTexts } from "./helpers";
import { m4 } from "./matrix3d"; // после изучение webgl на webgl fund, мне в наследство досталась библиотека которая умеет работает с 3д матрицами.
import appleObj from "./apple.obj"; // моделька яблока
import * as OBJ from "webgl-obj-loader"; // наша либа которая распарсит obj
function main() {
const apple = new OBJ.Mesh(appleObj); // загружаем модель
const canvas = createCanvas(); // создаю canvas и вставляю в body
const gl = canvas.getContext("webgl"); // получаю контекст
const program = createProgramFromTexts(gl, vertex, fragment); // создаю программу из шейдеров
gl.useProgram(program); // линкую программу к контексту
// получаю ссылку на атрибут
const positionLocation = gl.getAttribLocation(program, "a_position");
// у либы была готовая функция, которая за меня создавала буфер и прокидывала распарсенные данные в буферы. Из .obj можно было достать не только вершины, но и другие координаты которые могут быть полезны.
OBJ.initMeshBuffers(gl, apple);
gl.enableVertexAttribArray(positionLocation); // активирую атрибут, зачем это делать не знаю, но не сделаешь, ничего не заработает.
gl.vertexAttribPointer(
positionLocation,
apple.vertexBuffer.itemSize, // либа сама определяла сколько нужно атрибуту брать чисел, чтоб получить вершину
gl.FLOAT,
false, // отключаем нормализацию (это чтоб не пыталось конвертировать числа больше 1 в 1. Аля 255 -> 0.255.
0,
0
); // объясняю как атрибуту парсить данные
// получаем ссылку на глобальную переменную которая будет доступна внутри шейдеров. В нее же мы будем прокидывать матрицы
const matrixLocation = gl.getUniformLocation(program, "u_matrix");
let translation = [canvas.width / 2, 400, 0]; // смещаю на центр экрана по вертикали и 400 px вниз
let rotation = [degToRad(180), degToRad(0), degToRad(0)]; // вращение по нулям
let scale = [5, 5, 5]; // увеличиваю модельку в 5 раз. scaleX, scaleY, scaleZ
// выставляю вью порт
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.DEPTH_TEST); // включаем специальный флаг, который заставляет проверять видеокарту уровень вложенности и если какой-то треугольник перекрывает другой, то другой не будет рисоваться, потому, что он не виден.
function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // очищаем канвас на каждый рендер
const matrix = m4.multiply(
m4.identity(), // создаем единичную матрицу. Матрицу у которой все значения по умолчанию.
m4.orthographic(
0,
gl.canvas.width,
gl.canvas.height,
0,
400,
-400
), // Создаем матрицу которая конвертирует неудобные размеры модельки яблока в координатное пространство -1 до 1.
m4.translation(...translation), // перемещаем модельку
m4.xRotation(rotation[0]), // крутим по X
m4.yRotation(rotation[1]), // крутим по Y
m4.zRotation(rotation[2]), // крутим по Z
m4.scaling(...scale) // увеличиваем модельку
); // перемножаем матрицы друг на друга, чтоб в конце получить 1 матрицу которую и прокинем в шейдер
gl.uniformMatrix4fv(matrixLocation, false, matrix); // прокидываем матрицу
// подключаю буфер с индексами
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, apple.indexBuffer);
// рисуем яблоко треугольниками с помощью индексов
gl.drawElements(
gl.TRIANGLES,
apple.indexBuffer.numItems,
gl.UNSIGNED_SHORT,
0
);
}
drawScene();
// Тут код который настраивает всякие слайдеры, чтоб изменять матрицы.
// ...
//
}
main();
gl.TRIANGLES
на gl.LINES
. И получил следующий результат:у меня начали появляться подозрения, что рисовать фигуры можно только через треугольники.
precision mediump float;
attribute vec4 a_position;
attribute vec2 a_texture_coords; // текстурные координаты из модели
uniform mat4 u_matrix;
varying vec2 v_texture_coords;
void main(){
gl_Position = u_matrix * a_position;
v_texture_coords = a_texture_coords; // прокидываем во фрагментный шейдер
}
precision mediump float;
varying vec2 v_texture_coords; // координаты из вершины
uniform sampler2D u_texture; // текстура
void main(){
gl_FragColor = texture2D(u_texture, v_texture_coords);
}
//...
const textureCoordsLocation = gl.getAttribLocation(
program,
"a_texture_coords"
); // получили ссылку на новый атрибут
// ...
gl.enableVertexAttribArray(textureCoordsLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, apple.textureBuffer); // забиндили буфер которая выдала либа из модели
gl.vertexAttribPointer(
textureCoordsLocation,
apple.textureBuffer.itemSize,
gl.FLOAT,
false,
0,
0
);
const texture = gl.createTexture(); // запрашиваем место для текстуры
gl.bindTexture(gl.TEXTURE_2D, texture); // биндим
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
1,
1,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255])
); // сначала прокидываем пустышку, пока грузится текстура
const image = new Image();
image.src = textureImg; // загружаем текстуру
image.onload = () => {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(
gl.TEXTURE_2D,
gl.TEXTURE_MIN_FILTER,
gl.LINEAR_MIPMAP_LINEAR
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); // какие-то неведомые настройки, чтоб все было круто
drawScene();
};
// ...
const textureLocation = gl.getUniformLocation(program, "u_texture");
function drawScene() {
// ...
gl.uniform1i(textureLocation, 0);
// ...
}
Распарси .obj сам и получили нужные значения.