生成的多维数据集映射(Quad)Sphere

时间:2016-07-09 13:00:59

标签: c# opengl 3d geometry procedural-generation

我一直试图通过对四边形进行细分然后将其转换为球体来以编程方式生成球体。虽然纹理坐标和位置都是正确的,但最终法线是错误的。

用于计算每个顶点法线的公式是标准化的[v1-v2] x [v1-v3]标准。但是,当使用仅输出法线的着色器时,球体将呈现为纯黑色。 Black sphere with bad normals

用于生成球体的代码如下所示,以及将球体转储为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();
        }
    }
}

1 个答案:

答案 0 :(得分:1)

正如@Reto Koradi所说,球体的法线等于位置。 因此,CalculateNormals方法可以简化为以下内容。不需要对各个三角形进行计算。

self.ref.child("Lists").child(user!.uid).child(item).setValue(true)