您正在寻找的技术称为贴花。
您必须提取要绘制圆圈的地形部分,将适当的纹理应用到该部分并将其与地形混合绘制。
对于基于统一网格的地形,如下所示:
你有贴花的中心位置和它的半径。然后您可以确定网格中的最小和最大行/列,以便单元格包括每个绘制的区域。从这些顶点创建一个新的顶点缓冲区。可以从高度图中读取位置。您必须更改纹理坐标,以便将纹理放置在正确的位置。假设中心位置有坐标(0.5, 0.5)
,中心位置+(半径,半径)有坐标(1, 1)
等等。有了它,您应该能够找到每个顶点的纹理坐标的方程。
在上面的例子中,左上角的红色顶点的纹理坐标约为(-0.12, -0.05)
然后你有地形的子网格。将贴花纹理应用到它。设置适当的深度偏差(您必须尝试一些值)。在大多数情况下,负的 SlopeScaleDepthBias 会起作用。关闭采样器中的纹理坐标环绕。绘制子网格。
这是我为此目的编写的一些 VB SlimDX 代码:
Public Sub Init()
Verts = (Math.Ceiling(2 * Radius / TriAngleWidth) + 2) ^ 2
Tris = (Math.Ceiling(2 * Radius / TriAngleWidth) + 1) ^ 2 * 2
Dim Indices(Tris * 3 - 1) As Integer
Dim curN As Integer
Dim w As Integer
w = (Math.Ceiling(2 * Radius / TriAngleWidth) + 2)
For y As Integer = 0 To w - 2
For x As Integer = 0 To w - 2
Indices(curN) = x + y * w : curN += 1
Indices(curN) = x + (y + 1) * w : curN += 1
Indices(curN) = (x + 1) + (y) * w : curN += 1
Indices(curN) = x + (y + 1) * w : curN += 1
Indices(curN) = (x + 1) + (y + 1) * w : curN += 1
Indices(curN) = (x + 1) + y * w : curN += 1
Next
Next
VB = New Buffer(D3DDevice, New BufferDescription(Verts * VertexPosTexColor.Struct.SizeOfBytes, ResourceUsage.Dynamic, BindFlags.VertexBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, VertexPosTexColor.Struct.SizeOfBytes))
IB = New Buffer(D3DDevice, New DataStream(Indices, False, False), New BufferDescription(4 * Tris * 3, ResourceUsage.Default, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 4))
End Sub
Public Sub Update()
Dim Vertex(Verts - 1) As VertexPosTexColor.Struct
Dim curN As Integer
Dim rad As Single 'The decal radius
Dim height As Single
Dim p As Vector2
Dim yx, yz As Integer
Dim t As Vector2 'texture coordinates
Dim center As Vector2 'decal center
For y As Integer = Math.Floor((center.Y - rad) / TriAngleWidth) To Math.Floor((center.Y - rad) / TriAngleWidth) + Math.Ceiling(2 * rad / TriAngleWidth) + 1
For x As Integer = Math.Floor((center.X - rad) / TriAngleWidth) To Math.Floor((center.X - rad) / TriAngleWidth) + Math.Ceiling(2 * rad / TriAngleWidth) + 1
p.X = x * TriAngleWidth
p.Y = y * TriAngleWidth
yx = x : yz = y
If yx < 0 Then yx = 0
If yx > HeightMap.GetUpperBound(0) Then yx = HeightMap.GetUpperBound(0)
If yz < 0 Then yz = 0
If yz > HeightMap.GetUpperBound(1) Then yz = HeightMap.GetUpperBound(1)
height = HeightMap(yx, yz)
t.X = (p.X - center.X) / (2 * rad) + 0.5
t.Y = (p.Y - center.Y) / (2 * rad) + 0.5
Vertex(curN) = New VertexPosTexColor.Struct With {.Position = New Vector3(p.X, hoehe, p.Y), .TexCoord = t, .Color = New Color4(1, 1, 1, 1)} : curN += 1
Next
Next
Dim data = D3DContext.MapSubresource(VB, MapMode.WriteDiscard, MapFlags.None)
data.Data.WriteRange(Vertex)
D3DContext.UnmapSubresource(VB, 0)
End Sub
这是相应的 C# 代码。
public void Init()
{
Verts = Math.Pow(Math.Ceiling(2 * Radius / TriAngleWidth) + 2, 2);
Tris = Math.Pow(Math.Ceiling(2 * Radius / TriAngleWidth) + 1, 2) * 2;
int[] Indices = new int[Tris * 3];
int curN;
int w;
w = (Math.Ceiling(2 * Radius / TriAngleWidth) + 2);
for(int y = 0; y <= w - 2; ++y)
{
for(int x = 0; x <= w - 2; ++x)
{
Indices[curN] = x + y * w ; curN += 1;
Indices[curN] = x + (y + 1) * w ; curN += 1;
Indices[curN] = (x + 1) + (y) * w ; curN += 1;
Indices[curN] = x + (y + 1) * w ; curN += 1;
Indices[curN] = (x + 1) + (y + 1) * w ; curN += 1;
Indices[curN] = (x + 1) + y * w ; curN += 1;
}
}
VB = new Buffer(D3DDevice, new BufferDescription(Verts * VertexPosTexColor.Struct.SizeOfBytes, ResourceUsage.Dynamic, BindFlags.VertexBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, VertexPosTexColor.Struct.SizeOfBytes));
IB = new Buffer(D3DDevice, new DataStream(Indices, False, False), new BufferDescription(4 * Tris * 3, ResourceUsage.Default, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 4));
}
public void Update()
{
VertexPosTexColor.Struct[] Vertex = new VertexPosTexColor.Struct[Verts] ;
int curN;
float rad; //The decal radius
float height;
Vector2 p;
int yx, yz;
Vector2 t; //texture coordinates
Vector2 center; //decal center
for(int y = Math.Floor((center.Y - rad) / TriAngleWidth); y <= Math.Floor((center.Y - rad) / TriAngleWidth) + Math.Ceiling(2 * rad / TriAngleWidth) + 1; ++y)
for(int x = Math.Floor((center.X - rad) / TriAngleWidth); x <= Math.Floor((center.X - rad) / TriAngleWidth) + Math.Ceiling(2 * rad / TriAngleWidth) + 1; ++x)
{
p.X = x * TriAngleWidth;
p.Y = y * TriAngleWidth;
yx = x ; yz = y;
if( yx < 0)
yx = 0;
if (yx > HeightMap.GetUpperBound(0))
yx = HeightMap.GetUpperBound(0);
if (yz < 0)
yz = 0;
if (yz > HeightMap.GetUpperBound(1))
yz = HeightMap.GetUpperBound(1);
height = HeightMap[yx, yz];
t.X = (p.X - center.X) / (2 * rad) + 0.5;
t.Y = (p.Y - center.Y) / (2 * rad) + 0.5;
Vertex[curN] = new VertexPosTexColor.Struct() {Position = new Vector3(p.X, hoehe, p.Y), TexCoord = t, Color = New Color4(1, 1, 1, 1)}; curN += 1;
}
}
var data = D3DContext.MapSubresource(VB, MapMode.WriteDiscard, MapFlags.None);
data.Data.WriteRange(Vertex);
D3DContext.UnmapSubresource(VB, 0);
}