我一直试图通过对四边形进行细分然后将其转换为球体来以编程方式生成球体。虽然纹理坐标和位置都是正确的,但最终法线是错误的。
用于计算每个顶点法线的公式是标准化的[v1-v2] x [v1-v3]标准。但是,当使用仅输出法线的着色器时,球体将呈现为纯黑色。
用于生成球体的代码如下所示,以及将球体转储为obj文件的方法。如何修改它以产生正确的法线?
using System;
using System.IO;
using System.Collections.Generic;
public class QuadSphere
{
int primitiveCountSide;
VertexPositionNormalTexture[] vertices;
ushort[] indices;
public QuadSphere(int slices)
{
int planeVerts = (slices + 1) * (slices + 1);
vertices = new VertexPositionNormalTexture[planeVerts * 6];
int planeIndices = slices * slices * 6;
indices = new ushort[planeIndices * 6];
primitiveCountSide = planeIndices / 3;
//Generate planes
int vertexCount = 0;
//BOTTOM
TopBottom(1, slices, vertices, vertexCount);
vertexCount += planeVerts;
//TOP
TopBottom(-1, slices, vertices, vertexCount);
vertexCount += planeVerts;
//FRONT
FrontBack(-1, slices, vertices, vertexCount);
vertexCount += planeVerts;
//BACK
FrontBack(1, slices, vertices, vertexCount);
vertexCount += planeVerts;
//LEFT
LeftRight(-1, slices, vertices, vertexCount);
vertexCount += planeVerts;
//RIGHT
LeftRight(1, slices, vertices, vertexCount);
vertexCount += planeVerts;
//Generate indices
int indexCount = 0;
int baseVert = 0;
//BOTTOM
Indices(2, 1, 0, 1, 2, 3, slices, ref indexCount, indices, baseVert);
baseVert += planeVerts;
//TOP
Indices(0, 1, 2, 3, 2, 1, slices, ref indexCount, indices, baseVert);
baseVert += planeVerts;
//FRONT
Indices(2, 1, 0, 1, 2, 3, slices, ref indexCount, indices, baseVert);
baseVert += planeVerts;
//BACK
Indices(0, 1, 2, 3, 2, 1, slices, ref indexCount, indices, baseVert);
baseVert += planeVerts;
//LEFT
Indices(2, 1, 0, 1, 2, 3, slices, ref indexCount, indices, baseVert);
baseVert += planeVerts;
//RIGHT
Indices(0, 1, 2, 3, 2, 1, slices, ref indexCount, indices, baseVert);
//Transform Cube to Sphere
for (int i = 0; i < vertices.Length; i++)
{
float x = vertices[i].Position.X;
float y = vertices[i].Position.Y;
float z = vertices[i].Position.Z;
vertices[i].Position = new Vector3(
(float)(x * Math.Sqrt(1.0 - (y * y / 2.0) - (z * z / 2.0) + (y * y * z * z / 3.0))),
(float)(y * Math.Sqrt(1.0 - (z * z / 2.0) - (x * x / 2.0) + (z * z * x * x / 3.0))),
(float)(z * Math.Sqrt(1.0 - (x * x / 2.0) - (y * y / 2.0) + (x * x * y * y / 3.0)))
);
}
//Calculate Normals
CalculateNormals(vertices, indices);
}
void TopBottom(int Y, int slices, VertexPositionNormalTexture[] vertices, int vertexCount)
{
int width = slices + 1, height = slices + 1;
float advance = (2f / slices);
float tadvance = (1f / slices);
for (int z = 0; z < height; z++)
{
int basev = vertexCount + (z * width);
for (int x = 0; x < width; x++)
{
int index = basev + x;
vertices[index] = new VertexPositionNormalTexture(
new Vector3(
-1 + advance * x,
Y,
-1 + advance * z
),
Vector3.Zero,
new Vector2(
tadvance * x,
(Y == -1) ? tadvance * z : 1 - (tadvance * z)
)
);
}
}
}
void FrontBack(int Z, int slices, VertexPositionNormalTexture[] vertices, int vertexCount)
{
int width = slices + 1, height = slices + 1;
float advance = (2f / slices);
float tadvance = (1f / slices);
for (int z = 0; z < height; z++)
{
int basev = vertexCount + (z * width);
for (int x = 0; x < width; x++)
{
int index = basev + x;
vertices[index] = new VertexPositionNormalTexture(
new Vector3(
-1 + advance * x,
-1 + advance * z,
Z
),
Vector3.Zero,
new Vector2(
(Z == -1) ? 1 - (tadvance * x) : tadvance * x,
tadvance * z
)
);
}
}
}
void LeftRight(int X, int slices, VertexPositionNormalTexture[] vertices, int vertexCount)
{
int width = slices + 1, height = slices + 1;
float advance = (2f / slices);
float tadvance = (1f / slices);
for (int z = 0; z < height; z++)
{
int basev = vertexCount + (z * width);
for (int x = 0; x < width; x++)
{
int index = basev + x;
vertices[index] = new VertexPositionNormalTexture(
new Vector3(
X,
-1 + advance * x,
-1 + advance * z
),
Vector3.Zero,
new Vector2(
(X == -1) ? tadvance * z : 1 - (tadvance * z),
tadvance * x
)
);
}
}
}
void Indices(ushort t0, ushort t1, ushort t2, ushort t3, ushort t4, ushort t5, int slices, ref int i, ushort[] indices, int baseVert)
{
int width = slices + 1;
int height = slices;
ushort[] temp = new ushort[6];
for (int y = 0; y < height; y++)
{
int basev = baseVert + (y * width);
for (int x = 0; x < slices; x++)
{
//Allow defined winding order
temp[0] = (ushort)(basev + x);
temp[1] = (ushort)(basev + x + 1);
temp[2] = (ushort)(basev + width + x);
temp[3] = (ushort)(basev + width + x + 1);
indices[i++] = temp[t0];
indices[i++] = temp[t1];
indices[i++] = temp[t2];
indices[i++] = temp[t3];
indices[i++] = temp[t4];
indices[i++] = temp[t5];
}
}
}
public void Dump(string obj)
{
using (var writer = new StreamWriter(obj))
{
writer.WriteLine("#quadsphere obj");
foreach (var vert in vertices)
{
writer.WriteLine("v\t{0}\t{1}\t{2}", vert.Position.X, vert.Position.Y, vert.Position.Z);
}
writer.WriteLine();
foreach (var vert in vertices)
{
writer.WriteLine("vn\t{0}\t{1}\t{2}", vert.Normal.X, vert.Normal.Y, vert.Normal.Z);
}
writer.WriteLine();
foreach (var vert in vertices)
{
writer.WriteLine("vt\t{0}\t{1}", vert.TextureCoordinate.X, vert.TextureCoordinate.Y);
}
writer.WriteLine();
for (int i = 0; i < indices.Length / 3; i++)
{
writer.WriteLine("f\t{0}/{0}/{0}\t{1}/{1}/{1}\t{2}/{2}/{2}",
1 + indices[i * 3],
1 + indices[i * 3 + 1],
1 + indices[i * 3 + 2]
);
}
}
}
void CalculateNormals(VertexPositionNormalTexture[] array, ushort[] indices)
{
for (int i = 0; i < indices.Length / 3; i++)
{
var firstVec = array[indices[i * 3]].Position - array[indices[i * 3 + 1]].Position;
var secondVec = array[indices[i * 3]].Position - array[indices[i * 3 + 2]].Position;
var normal = Vector3.Cross(firstVec, secondVec);
normal.Normalize();
array[indices[i * 3]].Normal += normal;
array[indices[i * 3 + 1]].Normal += normal;
array[indices[i * 3 + 2]].Normal += normal;
}
for (int i = 0; i < array.Length; i++)
{
array[i].Normal.Normalize();
}
}
}
答案 0 :(得分:1)
正如@Reto Koradi所说,球体的法线等于位置。 因此,CalculateNormals方法可以简化为以下内容。不需要对各个三角形进行计算。
self.ref.child("Lists").child(user!.uid).child(item).setValue(true)