Строим 3D проекты – Шаг 5

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

Это элементарный прямой купол (призма, лежащая на одной из боковых граней).
Мы разбили фигуру на 18 треугольников (на рисунке показаны не все). Могли разбить на 8 треугольников, но при 18 точках мы можем настроить точнее цвет или форму всей фигуры.
Пусть точка 0 будет с координатами (0.0, 0.0, 0.0). Тогда остальные точки относительно точки 0 будут с координатами:
Т. 1 (1.0, 0.0, 0.0),
Т. 2 (2.0, 0.0, 0.0),
Т. 3 (0.5, 1.0, 0.0),
Т. 4 (1.5, 1.0, 0.0),
Т. 5 (1.0, 2.0, 0.0),
Т. 6 (0.0, 0.0, -2.0),
Т. 7 (0.1, 0.0, -2.0),
Т. 8 (2.0, 0.0, -2.0),
…
Используем буфер индексов, в котором будем манипулировать номерами вершин, а не их координатами. Тогда при использовании, например, точки Т.3, нам достаточно указать ее порядковый номер “3”, а не писать каждый раз (0.5, 1.0, 0.0).
Вносим изменения в программу.
var indexBuffer05pr = null; var angle05pr = 0;
initShaders05pr(); //[1]
setupBuffers05pr(); //[2]
getMatrixUniforms05pr();
(function animLoop05pr()
{
setupWebGL05pr(); //[3]
//setupDynamicBuffers05pr();
setMatrixUniforms05pr();
drawScene05pr(); //[4]
requestAnimationFrame( animLoop05pr, canvas05pr);
})();
//Colors.
var triangleVerticeColors =
[
//Prism colors ------------ -------------.
//front face
1.0, 0.0, 0.0, //R
1.0, 0.8, 0.0, //Orange
1.0, 1.0, 0.0, //Yellow
0.0, 1.0, 0.0, //G
0.0, 0.8, 1.0, //light-B
0.0, 0.0, 1.0, //B
//rear face
1.0, 1.0, 1.0, //White
0.0, 0.0, 0.0, //Black
0.6, 0.6, 0.6, //Dark gray
0.0, 0.0, 0.0, //Black
0.2, 0.2, 0.2, //Light gray
0.0, 0.0, 0.0, //Black
];
trianglesColorBuffer05pr = gl05pr.createBuffer();
gl05pr.bindBuffer( gl05pr.ARRAY_BUFFER, trianglesColorBuffer05pr );
gl05pr.bufferData( gl05pr.ARRAY_BUFFER, new Float32Array( triangleVerticeColors ), gl05pr.STATIC_DRAW );
//Vertices.
var triangleVertices =
[
//Prism's points coodinates ------------- ----------------.
//front face
0.0, 0.0, 0.0, //0th vertice
1.0, 0.0, 0.0, //1st vertice
2.0, 0.0, 0.0, //2nd vertice
0.5, 1.0, 0.0, //3rd vertice
1.5, 1.0, 0.0, //4th vertice
1.0, 2.0, 0.0, //5th vertice
//rear face
0.0, 0.0, -2.0, //6th vertice
1.0, 0.0, -2.0, //7th vertice
2.0, 0.0, -2.0, //8th vertice
0.5, 1.0, -2.0, //9th vertice
1.5, 1.0, -2.0, //10th vertice
1.0, 2.0, -2.0, //11th vertice
];
trianglesVerticeBuffer05pr = gl05pr.createBuffer();
gl05pr.bindBuffer( gl05pr.ARRAY_BUFFER, trianglesVerticeBuffer05pr );
gl05pr.bufferData( gl05pr.ARRAY_BUFFER, new Float32Array( triangleVertices ), gl05pr.STATIC_DRAW );
//Setup index buffer. 18 triangles.
var VertexIndices =
[
//front face
0,1,3,
1,3,4,
1,2,4,
3,4,5,
//rear face
6,7,9,
7,9,10,
7,8,10,
9,10,11,
//left side
0,3,6,
3,6,9,
3,5,9,
5,9,11,
//right side
2,4,8,
4,8,10,
4,5,10,
5,10,11,
//bottom faces
0,6,8,
8,2,0,
];
indexBuffer05pr = gl05pr.createBuffer();
gl05pr.bindBuffer( gl05pr.ELEMENT_ARRAY_BUFFER, indexBuffer05pr );
gl05pr.bufferData( gl05pr.ELEMENT_ARRAY_BUFFER, new Uint16Array( VertexIndices ), gl05pr.STATIC_DRAW );
//Will be used in drawScene05pr() function later.
indexBuffer05pr.num_pts = VertexIndices.length;
Все буферы задаются один раз, поэтому режим STATIC_DRAW.
Буфер индексов имеет тип ELEMENT_ARRAY_BUFFER.
В свойство
мы записали размер массива индексов, который используем позже.
function setupWebGL05pr()
{
//The default value to set the color buffer.
gl05pr.clearColor(0, 0.5, 0.5, 1);
//Clear color and depth buffers:
gl05pr.clear( gl05pr.COLOR_BUFFER_BIT | gl05pr.DEPTH_BUFFER_BIT );
//Hide the faces that should be hidden from view:
gl05pr.enable( gl05pr.DEPTH_TEST );
gl05pr.viewport(0, 0, canvas05pr.width, canvas05pr.height);
mat4.identity(mvMatrix05pr);
//Initial scene rotation.
mat4.translate(mvMatrix05pr, [0.0, 0.0, -7.0]);
mat4.rotate(mvMatrix05pr, 0.50, [1.0, 0.0, 0.0]);
mat4.rotate(mvMatrix05pr, -0.50, [0.0, 1.0, 0.0]);
//Plus scene rotation this frame.
mat4.rotate(mvMatrix05pr, angle05pr, [0.0, 1.0, 0.0]);
angle05pr += 0.01;
mat4.perspective(45, canvas05pr.width / canvas05pr.height, 0.1, 100.0, pMatrix05pr);
}
Также, здесь уже задействованы функции теста глубины, чтобы отображались только видимые поверхности :
gl05pr.clear( gl05pr.DEPTH_BUFFER_BIT); gl05pr.enable( gl05pr.DEPTH_TEST );
//[4]
function drawScene05pr()
{
//Move data into shader.
vertexPositionAttribute05pr = gl05pr.getAttribLocation( glProgram05pr, "aVertexPosition05pr" );
gl05pr.enableVertexAttribArray( vertexPositionAttribute05pr );
gl05pr.bindBuffer( gl05pr.ARRAY_BUFFER, trianglesVerticeBuffer05pr );
gl05pr.vertexAttribPointer( vertexPositionAttribute05pr, 3, gl05pr.FLOAT, false, 0, 0);
vertexColorAttribute05pr = gl05pr.getAttribLocation( glProgram05pr, "aVertexColor05pr" );
gl05pr.enableVertexAttribArray( vertexColorAttribute05pr );
gl05pr.bindBuffer( gl05pr.ARRAY_BUFFER, trianglesColorBuffer05pr );
gl05pr.vertexAttribPointer( vertexColorAttribute05pr, 3, gl05pr.FLOAT, false, 0, 0);
//Make draw.
//TYPE OF PRIMITIVE; FIRST POINT; NUM POINTS AFTER FIRST.
//gl05pr.drawArrays( gl05pr.TRIANGLES, 0, 12);
gl05pr.bindBuffer( gl05pr.ELEMENT_ARRAY_BUFFER, indexBuffer05pr );
//drawElements (mode, count, type, offset) - Called on a VBO of indices (of type ELEMENT_ARRAY_BUFFER ).
gl05pr.drawElements( gl05pr.TRIANGLES, indexBuffer05pr.num_pts, gl05pr.UNSIGNED_SHORT, 0);
}

При отключении теста глубины бывает сложно понять, что происходит:

В общем-то, только последняя строка функции drawScene05pr() рисует фигуру по буферу индексов. Для повышения производительности, можно вынести процедуру занесения данных в шейдеры в отдельную функцию и вызвать ее один раз до основного цикла, сразу после setupBuffers05pr (). Тогда коды функций будут такими:
function MoveStaticDataInShader05pr()
{
//Move data into shader.
vertexPositionAttribute05pr = gl05pr.getAttribLocation( glProgram05pr, "aVertexPosition05pr" );
gl05pr.enableVertexAttribArray( vertexPositionAttribute05pr );
gl05pr.bindBuffer( gl05pr.ARRAY_BUFFER, trianglesVerticeBuffer05pr );
gl05pr.vertexAttribPointer( vertexPositionAttribute05pr, 3, gl05pr.FLOAT, false, 0, 0);
vertexColorAttribute05pr = gl05pr.getAttribLocation( glProgram05pr, "aVertexColor05pr" );
gl05pr.enableVertexAttribArray( vertexColorAttribute05pr );
gl05pr.bindBuffer( gl05pr.ARRAY_BUFFER, trianglesColorBuffer05pr );
gl05pr.vertexAttribPointer( vertexColorAttribute05pr, 3, gl05pr.FLOAT, false, 0, 0);
gl05pr.bindBuffer( gl05pr.ELEMENT_ARRAY_BUFFER, indexBuffer05pr );
}
//[4]
function drawScene05pr()
{
//drawElements (mode, count, type, offset) - Called on a VBO of indices (of type ELEMENT_ARRAY_BUFFER ).
gl05pr.drawElements( gl05pr.TRIANGLES, indexBuffer05pr.num_pts, gl05pr.UNSIGNED_SHORT, 0);
}
Итак, к концу шага 05 мы создали программу, похожую на показанную на шаге 01. Остались только штрихи – разместить фигуру в центре координат и сместить точки Т.1 и Т.7, и вращать не только относительно оси Y. Развлекаемся как захотим, в общем.
Постойте, мы ведь реально создали с нуля подвижный объект в 3D мире! Это прорыв наших прошлых знаний и возможностей!
Что дальше? Вскоре мы научимся накладывать текстуры и управлять освещением. А ещё, немного позже – управлять тенями, улучшать производительность, интерактивно управлять объектами.
Удобно что есть оси координат и поэтому видно где находится фигура. Только в приведенной программе нет рисования осей, а у меня не получается ( Жду следующий шаг!