Подумать только

logoas CogITas

Dubito - Ergo - Sum

× Error from canvas.getContext(): Maybe your browser or hardware (GPU) does not appear to support WebGL. The Canvas for WebGL below will be empty.
Mouse и Num Lock

Последний шаг на пути к мечте

Строим 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).

 

Вносим изменения в программу.

  1. Добавим инициализацию глобальных переменных indexBuffer05pr, в которой будем хранить буфер индексов, и angle05pr, в которой будем хранить угол поворота сцены:
var indexBuffer05pr = null;
var angle05pr = 0;
   Ко всем собственным глобальным переменным и ко всем названиям собственных  функций я добавил текстовый паттерн «05pr». Так легче из текста программы воспринимать функции, созданные нами. Также по названиям сразу понятно, что это программа для шага 05.

 

  1. В данной программе мы не будем менять расположение вершин в пространстве, а будем двигать всю сцену. Поэтому функция setupDynamicBuffers05pr нам не понадобится, главный цикл будет такой:
initShaders05pr();   //[1]
		setupBuffers05pr();  //[2]
		getMatrixUniforms05pr();
	(function animLoop05pr()
	{
		setupWebGL05pr(); //[3]
		//setupDynamicBuffers05pr();

		setMatrixUniforms05pr();

		drawScene05pr(); //[4]
		requestAnimationFrame( animLoop05pr, canvas05pr);
	})();
  1. Выставляем как и раньше буферы цвета и вершин, а также новый буфер индексов:
                   //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.

   В свойство

indexBuffer05pr.num_pts

мы записали размер массива индексов, который используем позже.

  1. Каждый кадр в цикле мы поворачиваем сцену на угол angle05pr:
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 );
  1. Мы все подготовили, осталось нарисовать сцену:
//[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);
}

Error from Html: Your browser does not support the HTML5 canvas element.
 

Вращение 3D объекта
Вращение 3D объекта

 

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

Без теста глубины
Без теста глубины

 

                В общем-то, только последняя строка функции 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 мире! Это прорыв наших прошлых знаний и возможностей!

   Что дальше? Вскоре мы научимся накладывать текстуры и управлять освещением. А ещё, немного позже – управлять тенями, улучшать производительность, интерактивно управлять объектами.

 

<< Предыдущий шаг

Следующий шаг >>

Все статьи


Комментарии к “Последний шаг на пути к мечте”

  • Анна Апрельская говорит:

    Удобно что есть оси координат и поэтому видно где находится фигура. Только в приведенной программе нет рисования осей, а у меня не получается ( Жду следующий шаг!

    Ответить
    • Андрей Свирский говорит:

      Как временное решение, оси и подписи я нарисовал отдельными очень узкими треугольниками — выглядят как линии. Получилось громоздко. Если будет лучшее решение, напишу в отдельной статье.

      Ответить
  • Оставить комментарий для Анна Апрельская Отменить

    Ваш email не будет отображаться. Обязательные поля помечены *

    (Чтобы установить аватар, необходимо зарегистрировать свой e-mail на gravatar.com. Как это сделать, написано в статье http://cogitas.ru/robots-avatar-icon-wordpress.html)