Предварительные знания
Предполагается, что читатель знаком с основами 3D-графики, включая понятия «сетка» (mesh) и «текстура» (textures).
Задача
Рассмотрим задачу построения трехмерного графика функции как, например, sin(x*x+z*z)/(x*x+z*z) с использованием цветовой палитры:

Генерация сетки
Самым простом способом генерации сетки является использование Data.Points и Data.TriangleIndices объекта класса TMesh. Эти два свойства имеют строковой тип, поэтому они подвергаются синтаксическому анализу и разбору («парсингу») для генерации сетки в design-time (или в design-time, если они заданы именно на этом этапе). Этот синтаксический анализ требует определенного времени, что в данном случае приблизительно в 65 медленнее, чем использование внутренних буферов. Поэтому вместо него мы будем использовать не-published свойства Data.VertexBuffer и Data.IndexBuffer.
В нашем примере мы будем выполнять итерации от -30 до +30 по оси Х и в том же интервале по оси Z. Мы будем визуализировать функцию, которая рассчитываем значение Y для каждой точки.
Шаг 1: Генерация «проволочной рамки»
Картинка ниже демонстрирует достаточно разреженную «проволочную рамку», описывающую поверхность f = exp(sin x + cos z). Красным цветом здесь выделена один из четырехугольников. Каждый четырехугольник разделен на два треугольника для того, что образует необходимую нам сетку. Сетка состоит из всех треугольников, которые мы получаем при итерации по плоскости XZ.

Обозначим углы этого четырёхугольника P0, P1, P2 и P3:

Теперь два полученных треугольника можно обозначить как (P1, P2, P3) и (P3, P0, P1).
Выберем произвольное значение u на оси X в выбранном диапазоне, а v – произвольное значение на оси Z. Обозначим d – приращение значений координат на каждом шаге. Следующий код вычисляет четыре искомых точки на плоскости XZ:
P[0].x := u;
P[0].z := v;
P[1].x := u+d;
P[1].z := v;
P[2].x := u+d;
P[2].z := v+d;
P[3].x := u;
P[3].z := v+d;
Теперь мы можем рассчитать соответствующие значения функции Y в каждой точки, где f – данная функция f(x,z).
P[0].y := f(P[0].x,P[0].z);
P[1].y := f(P[1].x,P[1].z);
P[2].y := f(P[2].x,P[2].z);
P[3].y := f(P[3].x,P[3].z);
Теперь точки полностью определены по трём координатам. Теперь загружаем эти значения в сетку.
with
VertexBuffer do
begin
Vertices[0] := P[0];
Vertices[1] := P[1];
Vertices[2] := P[2];
Vertices[3] := P[3];
end
;
Эта часть кода выглядит достаточно просто. Теперь нужно сообщить сетке, какие точки образуют треугольники. Мы это делаем следующим образом:
IndexBuffer[0] := 1;
IndexBuffer[1] := 2;
IndexBuffer[2] := 3;
IndexBuffer[3] := 3;
IndexBuffer[4] := 0;
IndexBuffer[5] := 1;
Шаг 2: Генерация текстуры
Для того, чтобы задать сетке определенный цвет, мы создадим текстурный растр, который выглядит следующим образом:

Это – простая модель «Hue, Saturation, Lightness» (HSL), где значение «тона» изменяется от 0 до 359 градусов. «Насыщенность» и «светлота» остаются постоянными.
Для генерации текстуры используется следующий код:
BMP := TBitmap.Create(1,360);
for
k := 0 to
359 do
BMP.Pixels[0,k] := HSLtoRGB(k/360,0.75,0.5);
Шаг 3: Наложение текстуры на «проволочную рамку»
На последнем этапе нам нужно наложить текстуру на сетку. Это делается с использованием массива TexCoord0. Каждый элемент в массиве TexCoord0 представляет собой точку в прямоугольной системе координат (0,0)-(1,1). Так как мы накладываем текстуру, которая представляет собой линию, значение координаты X всегда равно 0. Координата Y получает своё значение в диапазоне (0,1), что соответствует следующему коду:
with
VertexBuffer do
begin
TexCoord0[0] := PointF(0,(P[0].y+35)/45);
TexCoord0[1] := PointF(0,(P[1].y+35)/45);
TexCoord0[2] := PointF(0,(P[2].y+35)/45);
TexCoord0[3] := PointF(0,(P[3].y+35)/45);
end
;
Сборка всего кода в одном вместе
Полный исходный код для генерации сетки представлен ниже:
function
f(x,z : Double) : Double;
var
temp : Double;
begin
temp := x*x+z*z;
if
temp < Epsilon then
temp := Epsilon;
Result := -2000*Sin(temp/180*Pi)/temp;
end
;
procedure
TForm1.GenerateMesh;
const
MaxX = 30;
MaxZ = 30;
var
u, v : Double;
P : array
[0..3] of
TPoint3D;
d : Double;
NP, NI : Integer;
BMP : TBitmap;
k : Integer;
begin
Mesh1.Data.Clear;
d := 0.5;
NP := 0;
NI := 0;
Mesh1.Data.VertexBuffer.Length := Round(2*MaxX*2*MaxZ/d/d)*4;
Mesh1.Data.IndexBuffer.Length := Round(2*MaxX*2*MaxZ/d/d)*6;
BMP := TBitmap.Create(1,360);
for
k := 0 to
359 do
BMP.Pixels[0,k] := CorrectColor(HSLtoRGB(k/360,0.75,0.5));
u := -MaxX;
while
u < MaxX do
begin
v := -MaxZ;
while
v < MaxZ do
begin
P[0].x := u;
P[0].z := v;
P[1].x := u+d;
P[1].z := v;
P[2].x := u+d;
P[2].z := v+d;
P[3].x := u;
P[3].z := v+d;
P[0].y := f(Func,P[0].x,P[0].z);
P[1].y := f(Func,P[1].x,P[1].z);
P[2].y := f(Func,P[2].x,P[2].z);
P[3].y := f(Func,P[3].x,P[3].z);
with
Mesh1.Data do
begin
with
VertexBuffer do
begin
Vertices[NP+0] := P[0];
Vertices[NP+1] := P[1];
Vertices[NP+2] := P[2];
Vertices[NP+3] := P[3];
end
;
with
VertexBuffer do
begin
TexCoord0[NP+0] := PointF(0,(P[0].y+35)/45);
TexCoord0[NP+1] := PointF(0,(P[1].y+35)/45);
TexCoord0[NP+2] := PointF(0,(P[2].y+35)/45);
TexCoord0[NP+3] := PointF(0,(P[3].y+35)/45);
end
;
IndexBuffer[NI+0] := NP+1;
IndexBuffer[NI+1] := NP+2;
IndexBuffer[NI+2] := NP+3;
IndexBuffer[NI+3] := NP+3;
IndexBuffer[NI+4] := NP+0;
IndexBuffer[NI+5] := NP+1;
end
;
NP := NP+4;
NI := NI+6;
v := v+d;
end
;
u := u+d;
end
;
Mesh1.Material.Texture := BMP;
end
;
Демонстрационное приложение
Контакты
Если у вас есть вопросы, присылайте их на адрес aohlsson «собачка» embarcadero точка com.
Connect with Us