如何在opengl中平滑多个四边形的纹理

Posted

技术标签:

【中文标题】如何在opengl中平滑多个四边形的纹理【英文标题】:How to smooth texture across multiple quads in opengl 【发布时间】:2019-05-01 21:03:31 【问题描述】:

我正在尝试使用我找到并调整大小的木质位图对木桌模型进行纹理处理,但是我遇到的问题是,当纹理设置时,构成桌子表面的每个四边形都清晰可见应用。如何让它看起来像一个光滑的纹理?

void Table::DrawTableTop()

    glPushMatrix();
    //draw tabletop

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texid);

    glBegin(GL_QUADS);
    glMaterialfv(GL_FRONT, GL_SPECULAR, static_cast<GLfloat*>(specular));
    glMaterialf(GL_FRONT, GL_SHININESS, static_cast<GLfloat>(shininess));

    float unitx = 1.0f / (float)(xsize * 2 + 1);
    float unity = 1.0f / (float)(zsize * 2 + 1);

    for (int i = -xsize; i <= xsize; i++)
    
        for (int j = -zsize; j <= zsize; j++)
        
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, static_cast<GLfloat*>(wDiffuse));
            //top
            glNormal3f(0.0f, 1.0f, 0.0f);
            if (tex) glTexCoord2f(unitx * static_cast<float>(i + 20 + 1), unity * static_cast<float>(j + 10));
            glVertex3f(scale[0] * static_cast<float>(i) + scale[0], 0, scale[2] * static_cast<float>(j) + scale[2]);
            if (tex) glTexCoord2f(unitx * static_cast<float>(i + 20 + 1), unity * static_cast<float>(j + 10 + 1));
            glVertex3f(scale[0] * static_cast<float>(i) + scale[0], 0, scale[2] * static_cast<float>(j));
            if (tex) glTexCoord2f(unitx * static_cast<float>(i + 20), unity * static_cast<float>(j + 10 + 1));
            glVertex3f(scale[0] * static_cast<float>(i), 0, scale[2] * static_cast<float>(j));
            if (tex) glTexCoord2f(unitx * static_cast<float>(i + 20 ), unity * static_cast<float>(j + 10));
            glVertex3f(scale[0] * static_cast<float>(i), 0, scale[2] * static_cast<float>(j) + scale[2]);
        
    

    glEnd();

    glBindTexture(GL_TEXTURE_2D, 0);
    glDisable(GL_TEXTURE_2D);

    glPopMatrix();

xsize 和 zsize 的值分别是 20 和 10

我希望纹理看起来好像加载到一个大四边形上,但显然使用多个四边形来改善照明等。

这是纹理图像的样子:

【问题讨论】:

"...每个四边形...在应用纹理时清晰可见。如何使它看起来像一个光滑的纹理?" - 所以纹理是不是无缝的? 我添加了一个图片链接以更好地展示我在帖子中的意思 纹理坐标必须在 [0, 1] 范围内 (0, 0) 是纹理的左下角, (1, 1) 是纹理的右上角。见How do opengl texture coordinates work?。 据我所知,我的纹理坐标在那个范围内 【参考方案1】:

图块上的纹理沿 y 轴翻转。更改纹理坐标:

if (tex) 
    glTexCoord2f(
        unitx * static_cast<float>(i + 20 + 1),
        unity * static_cast<float>(j + 10 + 1));
glVertex3f(scale[0] * static_cast<float>(i) + scale[0], 0, scale[2] * static_cast<float>(j) + scale[2]);

if (tex) 
    glTexCoord2f(
        unitx * static_cast<float>(i + 20 + 1),
        unity * static_cast<float>(j + 10));
glVertex3f(scale[0] * static_cast<float>(i) + scale[0], 0, scale[2] * static_cast<float>(j));

if (tex)
    glTexCoord2f(
        unitx * static_cast<float>(i + 20),
        unity * static_cast<float>(j + 10));
glVertex3f(scale[0] * static_cast<float>(i), 0, scale[2] * static_cast<float>(j));

if (tex)
    glTexCoord2f(
        unitx * static_cast<float>(i + 20 ),
        unity * static_cast<float>(j + 10 + 1));
glVertex3f(scale[0] * static_cast<float>(i), 0, scale[2] * static_cast<float>(j) + scale[2]);

【讨论】:

【参考方案2】:

你搞砸了第二个纹理坐标。当顶点位置有+ scale[0] 时将其设置为j + 20,当顶点位置没有+ scale[0] 时将其设置为j + 20 + 1。结果,您的所有条带都“倒置”了。

要修复它,请使用:

if (tex) glTexCoord2f(unitx * static_cast<float>(i + 20 + 1), unity * static_cast<float>(j + 10 + 1));
glVertex3f(scale[0] * static_cast<float>(i) + scale[0], 0, scale[2] * static_cast<float>(j) + scale[2]);
if (tex) glTexCoord2f(unitx * static_cast<float>(i + 20 + 1), unity * static_cast<float>(j + 10));
glVertex3f(scale[0] * static_cast<float>(i) + scale[0], 0, scale[2] * static_cast<float>(j));
if (tex) glTexCoord2f(unitx * static_cast<float>(i + 20), unity * static_cast<float>(j + 10));
glVertex3f(scale[0] * static_cast<float>(i), 0, scale[2] * static_cast<float>(j));
if (tex) glTexCoord2f(unitx * static_cast<float>(i + 20 ), unity * static_cast<float>(j + 10 + 1));
glVertex3f(scale[0] * static_cast<float>(i), 0, scale[2] * static_cast<float>(j) + scale[2]);

不过,更好的方法是提取顶点位置计算如下:

struct Vertex 
    float texcoord[2];
    float position[3];
;

Vertex vertex_at(float scale[3], float unitx, float unity, int i, int j)

    Vertex ret;
    ret.texcoord[0] = unitx*(i + 20);
    ret.texcoord[1] = unity*(j + 10);
    ret.position[0] = scale[0]*i;
    ret.position[1] = 0;
    ret.position[2] = scale[2]*j;
    return ret;

然后像这样使用它:

for (int i = -xsize; i <= xsize; i++)

    for (int j = -zsize; j <= zsize; j++)
    
        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, static_cast<GLfloat*>(wDiffuse));
        //top
        glNormal3f(0.0f, 1.0f, 0.0f);
        Vertex v[4] = 
            vertex_at(scale, unitx, unity, i + 1, j + 1),
            vertex_at(scale, unitx, unity, i + 1, j),
            vertex_at(scale, unitx, unity, i, j),
            vertex_at(scale, unitx, unity, i, j + 1),
        ;
        for(int k = 0; k < 4; ++k) 
            if (tex) glTexCoord2fv(v[k].texcoord);
            glVertex3fv(v[k].position);
        
    

这样更容易跟踪正在发生的事情,并且您离使用 VAO 更近了一步:只需将所有这些 Vertex 结构捆绑到缓冲区中并设置属性指针。

【讨论】:

以上是关于如何在opengl中平滑多个四边形的纹理的主要内容,如果未能解决你的问题,请参考以下文章

如何在OpenGL中将像素作为纹理绘制到多边形?

如何在 C++ 中将位图绘制为 OpenGL 纹理?

在 OpenGL 中平滑面部边缘的技术

如何在片段着色器中平铺部分纹理

如何在 1D 缓冲区中生成 2D 纹理并将其加载到 OpenGL 中?

如何将 UV 纹理从搅拌机加载到 opengl android java