【问题标题】:Incorrect normals on generated Cube-mapped (Quad) Sphere生成的立方体映射(四边形)球体上的法线不正确
【发布时间】:2016-07-09 13:00:58
【问题描述】:

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

用于计算每个顶点法线的公式是标准化的标准 [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();
        }
    }
}

【问题讨论】:

  • 我没有尝试遵循您的代码,但是您为什么要执行计算来获取球体的法线?对于球体,法向量与位置相同。

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


【解决方案1】:

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

void CalculateNormals(VertexPositionNormalTexture[] vertices)
{
    for(int i = 0; i < vertices.Length; i++)
        vertices[i].Normal = vertices[i].Position;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-12
    • 2017-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多