简体   繁体   English

OpenGL球体顶点和UV坐标

[英]OpenGL Sphere vertices and UV coordinates

I know there are many similar questions for this issue, such as this one , but I can't seem to figure out what is going wrong in my program. 我知道这个问题有很多类似的问题,例如this ,但是我似乎无法弄清楚程序中出了什么问题。

I am attempting to create a unit sphere using the naive longitude/latitude method, then I attempt to wrap a texture around the sphere using UV coordinates. 我试图使用朴素的经度/纬度方法创建一个单位球体,然后尝试使用UV坐标将纹理包裹在球体周围。

I am seeing the classic vertical seam issue, but I'm also some strangeness at both poles. 我看到了经典的垂直接缝问题,但是我在两个方面都有些陌生。

North Pole... 北极... 北极

South Pole... 南极... 在此处输入图片说明

Seam... 接缝... 在此处输入图片说明

The images are from a sphere with 180 stacks and 360 slices. 图像来自具有180个堆栈和360个切片的球体。

I create it as follows. 我如下创建它。

First, here are a couple of convenience structures I'm using... 首先,这里有几个我正在使用的便利结构...

struct Point {
    float x;
    float y;
    float z;
    float u;
    float v;
};

struct Quad {
    Point lower_left;  // Lower left corner of quad
    Point lower_right; // Lower right corner of quad
    Point upper_left;  // Upper left corner of quad
    Point upper_right; // Upper right corner of quad
};

I first specify a sphere which is '_stacks' high and '_slices' wide. 我首先指定一个球体,球体的高度为“ _stacks”,宽度为“ _slices”。

float* Sphere::generate_glTriangle_array(int& num_elements) const
{
    int elements_per_point  = 5; //xyzuv
    int points_per_triangle = 3;
    int triangles_per_mesh = _stacks * _slices * 2; // 2 triangles makes a quad
    num_elements = triangles_per_mesh * points_per_triangle * elements_per_point;

    float *buff = new float[num_elements];
    int i = 0;

    Quad q;

    for (int stack=0; stack<_stacks; ++stack)
    {
        for (int slice=0; slice<_slices; ++slice)
        {
            q = generate_sphere_quad(stack, slice);
            load_quad_into_array(q, buff, i);
        }
    }

    return buff;
}

Quad Sphere::generate_sphere_quad(int stack, int slice) const
{
    Quad q;

    std::cout << "Stack " << stack << ", Slice: " << slice << std::endl;

    std::cout << "   Lower left...";
    q.lower_left = generate_sphere_coord(stack, slice);
    std::cout << "   Lower right...";
    q.lower_right = generate_sphere_coord(stack, slice+1);
    std::cout << "   Upper left...";
    q.upper_left = generate_sphere_coord(stack+1, slice);
    std::cout << "   Upper right...";
    q.upper_right = generate_sphere_coord(stack+1, slice+1);
    std::cout << std::endl;

    return q;
}

Point Sphere::generate_sphere_coord(int stack, int slice) const
{
    Point p;

    p.y = 2.0 * stack / _stacks - 1.0;

    float r = sqrt(1 - p.y * p.y);
    float angle = 2.0 * M_PI * slice / _slices;

    p.x = r * sin(angle);
    p.z = r * cos(angle);

    p.u = (0.5 + ( (atan2(p.z, p.x)) / (2 * M_PI) ));
    p.v = (0.5 + ( (asin(p.y)) / M_PI ));

    std::cout << " Point: (x: " << p.x << ", y: " << p.y << ", z: " << p.z << ") [u: " << p.u << ", v: " << p.v << "]" << std::endl;

    return p;
}

I then load my array, specifying vertices of two CCW triangles for each Quad... 然后我加载数组,为每个Quad指定两个CCW三角形的顶点。

void Sphere::load_quad_into_array(const Quad& q, float* buff, int& buff_idx, bool counter_clockwise=true)
{
    if (counter_clockwise)
    {
        // First triangle
        load_point_into_array(q.lower_left, buff, buff_idx);
        load_point_into_array(q.upper_right, buff, buff_idx);
        load_point_into_array(q.upper_left, buff, buff_idx);

        // Second triangle
        load_point_into_array(q.lower_left, buff, buff_idx);
        load_point_into_array(q.lower_right, buff, buff_idx);
        load_point_into_array(q.upper_right, buff, buff_idx);
    }
    else
    {
        // First triangle
        load_point_into_array(q.lower_left, buff, buff_idx);
        load_point_into_array(q.upper_left, buff, buff_idx);
        load_point_into_array(q.upper_right, buff, buff_idx);

        // Second triangle
        load_point_into_array(q.lower_left, buff, buff_idx);
        load_point_into_array(q.upper_right, buff, buff_idx);
        load_point_into_array(q.lower_right, buff, buff_idx);
    }
}

void Sphere::load_point_into_array(const Point& p, float* buff, int& buff_idx)
{
    buff[buff_idx++] = p.x;
    buff[buff_idx++] = p.y;
    buff[buff_idx++] = p.z;
    buff[buff_idx++] = p.u;
    buff[buff_idx++] = p.v;
}

My vertex and fragment shaders are simple... 我的顶点和片段着色器很简单...

// Vertex shader
#version 450 core

in vec3 vert;
in vec2 texcoord;

uniform mat4 matrix;

out FS_INPUTS {
   vec2 i_texcoord;
} tex_data;

void main(void) {
   tex_data.i_texcoord = texcoord;
   gl_Position = matrix * vec4(vert, 1.0);
}

// Fragment shader
#version 450 core

in FS_INPUTS {
   vec2 i_texcoord;
};

layout (binding=1) uniform sampler2D tex_id;

out vec4 color;

void main(void) {
   color = texture(tex_id, texcoord);
}

My draw command is: 我的绘制命令是:

glDrawArrays(GL_TRIANGLES, 0, num_elements/5);

Thanks! 谢谢!

First of all, this code does some funny extra work: 首先,此代码做了一些有趣的额外工作:

Point Sphere::generate_sphere_coord(int stack, int slice) const
{
    Point p;

    p.y = 2.0 * stack / _stacks - 1.0;

    float r = sqrt(1 - p.y * p.y);
    float angle = 2.0 * M_PI * slice / _slices;

    p.x = r * sin(angle);
    p.z = r * cos(angle);

    p.u = (0.5 + ( (atan2(p.z, p.x)) / (2 * M_PI) ));
    p.v = (0.5 + ( (asin(p.y)) / M_PI ));

    return p;
}

Calling cos and sin just to cal atan2 on the result is just extra work in the best case, and in the worst case you might get the wrong branch cuts. 在最好的情况下,调用cossin只是为了计算atan2的结果只是额外的工作,而在最坏的情况下,您可能会得到错误的分支剪切。 You can calculate pu directly from slice and slice instead. 您可以直接从sliceslice计算出pu

The Seam 接缝

You are going to have a seam in your sphere. 您将在球体中缝隙。 This is normal, most models will have a seam (or many seams) in their UV maps somewhere. 这是正常现象,大多数模型的UV贴图中的某处将有一个接缝(或多个接缝)。 The problem is that the UV coordinates should still increase linearly next to the seam. 问题在于,UV坐标在接缝附近仍应线性增加。 For example, think about a loop of vertices that go around the globe's equator. 例如,考虑围绕地球赤道的一圈顶点。 At some point, the UV coordinates will wrap around, something like this: 在某些时候,UV坐标将环绕,如下所示:

0.8, 0.9, 0.0, 0.1, 0.2

The problem is that you'll get four quads, but one of them will be wrong: 问题是您将得到四个四边形,但是其中一个将是错误的:

quad 1: u = 0.8 ... 0.9
quad 2: u = 0.9 ... 0.0 <<----
quad 3: u = 0.0 ... 0.1
quad 4: u = 0.1 ... 0.2

Look at how messed up quad 2 is. 看一下Quad 2到底是怎么弄糟的。 You will have to generate instead the following data: 您将不得不生成以下数据:

quad 1: u = 0.8 ... 0.9
quad 2: u = 0.9 ... 1.0
quad 3: u = 0.0 ... 0.1
quad 4: u = 0.1 ... 0.2

A Fixed Version 固定版

Here is a sketch of a fixed version. 这是固定版本的草图。

namespace {

const float pi = std::atan(1.0f) * 4.0f;

// Generate point from the u, v coordinates in (0..1, 0..1)
Point sphere_point(float u, float v) {
    float r = std::sin(pi * v);
    return Point{
        r * std::cos(2.0f * pi * u),
        r * std::sin(2.0f * pi * u),
        std::cos(pi * v),
        u,
        v
    };
}

}

// Create array of points with quads that make a unit sphere.
std::vector<Point> sphere(int hSize, int vSize) {
    std::vector<Point> pt;
    for (int i = 0; i < hSize; i++) {
        for (int j = 0; j < vSize; j++) {
            float u0 = (float)i / (float)hSize;
            float u1 = (float)(i + 1) / (float)hSize;
            float v0 = (float)j / (float)vSize;
            float v1 = (float)(j + 1) / float(vSize);
            // Create quad as two triangles.
            pt.push_back(sphere_point(u0, v0));
            pt.push_back(sphere_point(u1, v0));
            pt.push_back(sphere_point(u0, v1));
            pt.push_back(sphere_point(u0, v1));
            pt.push_back(sphere_point(u1, v0));
            pt.push_back(sphere_point(u1, v1));
        }
    }
}

Note that there is some easy optimization you could do, and also note that due to rounding errors, the seam might not line up quite correctly. 请注意,您可以执行一些简单的优化操作,还请注意,由于舍入错误,接缝可能无法正确对齐。 These are left as an exercise for the reader. 这些留给读者练习。

More Problems 更多问题

Even with the fixed version, you will likely see artifacts at the poles. 即使使用固定版本,您也可能会在两极看到工件。 This is because the screen space texture coordinate derivatives have a singularity at the poles. 这是因为屏幕空间纹理坐标导数在极点具有奇异性。

The recommended way to fix this is to use a cube map texture instead. 解决此问题的推荐方法是改用立方体贴图纹理。 This will also greatly simplify the sphere geometry data, since you can completely eliminate the UV coordinates and you won't have a seam. 这也将大大简化球体几何数据,因为您可以完全消除UV坐标并且没有接缝。

As a kludge, you can enable anisotropic filtering instead. 作为一种麻烦,您可以启用各向异性过滤。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM