【问题标题】:OpenGL Cube is not properly texturingOpenGL Cube 没有正确纹理化
【发布时间】:2017-04-28 23:12:30
【问题描述】:

我正在尝试对使用 VBO/VAO 制作的 3D 立方体进行纹理处理。然而,我只设法在立方体上正确地纹理 4/6 面。我尝试了多种解决方案(从多次定义顶点到尝试立方体映射——当使用讲师的解决方案时,只会导致立方体由于某种原因完全消失)

最初我可以正确地纹理正面、背面、顶面和底面。但是在处理了几个小时的坐标之后,我没有设法对前、后、左和右面(但不是顶部或底部)进行纹理处理。

如果有人可以帮助我(并请解释纹理坐标的实际工作原理 - 我在 2D Quad 上得到了这个想法,但在 3D VBO 上没有 - 或者更好的高质量教程等:))

下面是代码;

VBO 设置

// 每个顶点的位置向量

static float pyramidVertices[] =
{
    //Front
    0.0f, 0.0f, 0.0f, 1.0f, //BtmLeft
    1.0f, 0.0f, 0.0f, 1.0f, //BtmRight
    1.0f, 1.0f, 0.0f, 1.0f, //TopRight
    0.0f, 1.0f, 0.0f, 1.0f, //TopLeft
    //Back
    0.0f, 1.0f, -1.0f, 1.0f,    //TopLeft
    1.0f, 1.0f, -1.0f, 1.0f,    //TopRight
    1.0f, 0.0f, -1.0f, 1.0f,    //BottomRight
    0.0f, 0.0f, -1.0f, 1.0f //BottomLeft
};

#pragma region Pyramid Data
// Per-vertex colours (RGBA) floating point values
static float        pyramidColours[32] =
{
    1.0f, 0.0f, 0.0f, 1.0f,
    0.0f, 1.0f, 0.0f, 1.0f,
    0.0f, 0.0f, 1.0f, 1.0f,
    1.0f, 0.0f, 1.0f, 1.0f,
    0.0f, 1.0f, 1.0f, 1.0f,
    0.0f, 0.0f, 1.0f, 1.0f,
    1.0f, 0.0f, 1.0f, 1.0f,
    1.0f, 0.0f, 0.0f, 1.0f
};

// 5 faces each with 3 vertices (each face forms a triangle)
static unsigned short       pyramidVertexIndices[] =
{
    //Front
    0, 1, 2,
    2, 3, 0,
    //Left
    0, 3, 7,
    7, 3, 4,
    //Back
    4, 5, 6,
    6, 7, 4, 
    //Top
    4, 3, 5,
    5, 3, 2,
    //Right
    2, 1, 5,
    5, 1, 6,
    //Bottom
    6, 1, 7, 
    7, 1, 0
};

#pragma endregion
static float pyramidTexCoordArray[] =
{
    0.0f, 0.0f,
    1.0f, 0.0f,
    1.0f, 1.0f,
    0.0f, 1.0f,
    1.0f, 1.0f,
    0.0f, 1.0f,
    0.0f, 0.0f,
    1.0f, 0.0f
};

初始化

void init(void) {

    // Request an OpenGL 4.3 context with the Compatibility profile
    glutInitContextVersion(4, 3);
    glutInitContextProfile(GLUT_COMPATIBILITY_PROFILE);

    // Setup OpenGL Display mode - include MSAA x4
    glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE | GLUT_MULTISAMPLE);

    [...]
    [...]
    [...]

    texturedQuad = new CGTexturedQuad(wstring(L"Common\\Resources\\Textures\\bumblebee.png"));
    pyramidTexture = TextureLoader::fiLoadTexture(wstring(L"Common\\Resources\\Textures\\VBO\\sandstone.png"));
    exampleModel = new CGModel();
    importGSF(L"Common\\Resources\\Models\\dropship.gsf", exampleModel);

    // Setup VAO for pyramid object
    glGenVertexArrays(1, &pyramidVAO);
    glBindVertexArray(pyramidVAO);

    // Setup VBO for vertex position data
    glGenBuffers(1, &pyramidVertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, pyramidVertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(pyramidVertices), pyramidVertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)0); // attribute 0 gets data from bound VBO (so assign vertex position buffer to attribute 0)

    // Setup VBO for vertex colour data
    glGenBuffers(1, &pyramidColourBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, pyramidColourBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(pyramidColours), pyramidColours, GL_STATIC_DRAW);
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_TRUE, 0, (const GLvoid*)0); // attribute 1 gets colour data

    glGenBuffers(1, &pyramidTexCoordBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, pyramidTexCoordBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(pyramidTexCoordArray), pyramidTexCoordArray, GL_STATIC_DRAW);
    glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)0);

    // Enable vertex position and colour attribute arrays
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(3);

    // Setup VBO for face index array
    glGenBuffers(1, &pyramidIndexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pyramidIndexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(pyramidVertexIndices), pyramidVertexIndices, GL_STATIC_DRAW);

    // Unbind pyramid VAO (or bind another VAO for another object / effect)
    // If we didn't do this, we may alter the bindings created above.
    glBindVertexArray(0);


    glEnable(GL_NORMALIZE); // If we scale objects, ensure normal vectors are re-normalised to length 1.0 to keep lighting calculations correct (see lecture notes)
    //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Best colour interpolation results


    // Setup GL_LIGHT0
    glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);     // Setup ambient light
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);     // Setup diffuse light
    glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);   // Setup specular light

    glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, ca);
    glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, la);
    glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, qa);

    glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 15.0f);
    glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 0.0);

    // OpenGL provides a global ambient light component - we don't want this so set to zero
    GLfloat global_ambient[] = { 0.15f, 0.15f, 0.15f, 1.0f };
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);
    //
    // Load the shader we'll use for the pyramid object
    //

    err = ShaderLoader::createShaderProgram(string("Common\\Resources\\Shaders\\basic_texture.vs"), string("Common\\Resources\\Shaders\\basic_texture.fs"), &basicShader);
}

显示功能

void display(void) {

    // Clear the screen
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // Set viewport to the client area of the current window
    glViewport(0, 0, glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT));

    // Get view-projection transform as a GUMatrix4
    GUMatrix4 T = mainCamera->projectionTransform() * mainCamera->viewTransform();

    if (principleAxes)
        principleAxes->render(T);

    if (texturedQuad)
        texturedQuad->render(T * GUMatrix4::translationMatrix(0.5f, 0.5f, 0.0f));


    // Fixed function rendering (Compatability profile only) - use this since CGImport is written against OpenGL 2.1
    glUseProgram(0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMultMatrixf((const float*)mainCamera->projectionTransform().M);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMultMatrixf((const float*)mainCamera->viewTransform().M);
    glMultMatrixf((const float*)GUMatrix4::translationMatrix(0.0f, -0.15f, 0.0f).M);

    glEnable(GL_TEXTURE_2D);

    glPolygonMode(GL_FRONT, GL_FILL);

    if (exampleModel)
        exampleModel->renderTexturedModel();

    glDisable(GL_TEXTURE_2D);

    //Define position and direction (so appear at fixed point in scene)
    glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, lightDirection);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);

    // enable texturing
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    //
    // Pyramid VBO rendering
    //

    // Use basic shader for rendering pyramid (we'll look at this in more detail next week)
    glUseProgram(basicShader);

    static GLint mvpLocationPyramid = glGetUniformLocation(basicShader, "mvpMatrix");

    glUniformMatrix4fv(mvpLocationPyramid, 1, GL_FALSE, (const GLfloat*)&(T.M));

    GUMatrix4 pyramidModelTransform = GUMatrix4::translationMatrix(-0.0f, 0.0f, 0.0f) * GUMatrix4::scaleMatrix(2.0f, 2.0f, 2.0f);
    GUMatrix4 mvpPyramid = T * pyramidModelTransform;
    glUniformMatrix4fv(mvpLocationPyramid, 1, GL_FALSE, (const GLfloat*)&(mvpPyramid.M));

    // Bind VAO that contains all relevant pyramid VBO buffer and attribute pointer bindings
    glBindVertexArray(pyramidVAO);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, pyramidTexture);

    // Draw pyramid
    glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, (const GLvoid*)0);

    // Unbind pyramid VAO (or bind another VAO)
    glBindVertexArray(0);


    glutSwapBuffers();
}

片段着色器

#version 330

uniform sampler2D texture;

in vec2 texCoord;

layout (location=0) out vec4 fragColour;

void main(void) {

    vec4 texColor = texture2D(texture, texCoord);
    fragColour = texColor;
}

顶点着色器

#version 330

uniform mat4 mvpMatrix;

layout (location=0) in vec4 vertexPos;
layout (location=3) in vec2 vertexTexCoord;

out vec2 texCoord;

void main(void) {

    mat4 M;
    M[0] = vec4(1.0);

    ivec2 a = ivec2(1, 2);
    //vec3 b = vec3(2.0, 4.0, 1.0) + a;

    texCoord = vertexTexCoord;
    gl_Position = mvpMatrix * vertexPos;
}

结果立方体 (忽略灰色位,它只是导入“dropship.gsf”的 3D 模型)

【问题讨论】:

    标签: c++ opengl textures vbo vao


    【解决方案1】:

    这是意料之中的。问题是每个顶点只能有一个纹理坐标。为了解决这个问题,复制顶点,这样你就可以为共享相同位置的不同顶点分配不同的纹理坐标。

    我通常不会使用 8 个顶点,而是使用 24 个顶点:每个面 4 个。通过不共享面之间的顶点,您可以独立地对每个面进行纹理处理。

    从技术上讲,您仍然可以共享 一些 顶点,但是 24 个非常小,如果您想稍后添加它们,它可以让您正确获得法线向量。对于带有 UV 映射的平滑模型,通常有一个接缝(或多个接缝)的顶点是重复的,这可以在 UV 映射过程中在您的 3D 编辑器中创建。

    P.S. 变量命名为“金字塔”而不是“立方体”有点令人困惑。

    【讨论】:

    • 非常感谢!正如你提到的,我真的开始研究这个了!我会让你知道情况如何。并为命名道歉,我的最终目标是制作金字塔而不是立方体,但金字塔有问题所以认为立方体会更简单,所以我暂时改用制作立方体(当它工作时我打算将其转换为金字塔 - 但我现在跳过该阶段并直接前往金字塔。再次感谢!)
    • 您可能还会考虑(稍后)将所有属性放入交错数组中——这样您就有了类似float VertexData[][8] = { ..., { x, y, z, r, g, b, u, v }, ... }; 的东西。这意味着您只需要一个顶点缓冲区而不是三个,并且可以更轻松地在源代码中复制和粘贴顶点数据。您所要做的就是更改glVertexAttribPointer 的最后两个参数以使其工作。
    • 几何着色器也可以工作,这将是学习它们的好(简单)应用程序。
    • @AndonM.Coleman:几何着色器在一般情况下不起作用,它只是几何的一个怪癖,可以让你在这里做到这一点,你可以同样轻松地使用三平面纹理映射在片段着色器中。我不建议将此作为使用几何着色器的好案例。
    • @DietrichEpp 我听取了您对交错阵列的建议。现在一切正常。我的绝大多数金字塔现在都可以正常显示。但是(令人困惑和烦人)在我的金字塔底部有一个三角形没有渲染。最初我认为我没有分配足够的内存,但我现在分配的内存已经足够多(以及我认为的确切数量)并且它仍然没有渲染最后一个三角形......我不知道它是否不是渲染最后一个顶点,或者如果它没有渲染最后 3 个顶点。这不是一个大问题(我可以隐藏它),但它只是烦人
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-31
    • 2020-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多