在3D世界中渲染2D精灵?

时间:2010-11-26 01:53:52

标签: opengl-es webgl

如果我有一个精灵的png,我如何在OpenGL中渲染2D精灵?看图像作为我想要实现的效果的一个例子。另外我想在屏幕上覆盖武器,就像底部图像中的步枪一样。有谁知道我将如何实现这两种效果?任何帮助是极大的赞赏。

alt text

alt text

7 个答案:

答案 0 :(得分:8)

在3D术语中,这称为“广告牌”。广告牌是完全平坦的2D平面,上面有纹理,它总是面向相机。

请参阅此处获取纯粹的OpenGL实现:http://nehe.gamedev.net/data/articles/article.asp?article=19

几乎任何3D引擎都应该能够默认执行它们。例如,Ogre3D可以做到这一点。

答案 1 :(得分:2)

a)对于第一种情况:

那不是2D精灵。这些人似乎被渲染为单个四边形,其纹理具有某种透明度(alpha测试或alpha混合)。

无论如何,即使单个四边形仍然可以被视为3D对象,因此对于这种情况,您可能希望将其视为一个:跟踪其平移和旋转并以与任何其他3D对象相同的方式呈现它。

b)对于第二种情况:

如果您想要在没有任何透视变换的情况下将枪(2D图片,我按下)渲染到同一个地方,那么您可以使用与绘制GUI(等)相同的技术。看看我的帖子:

2D overlay on a 3D scene

答案 2 :(得分:1)

对于2D武器的叠加,您可以使用glOrtho作为摄像机视图。

答案 3 :(得分:1)

您可以创建一个3d四元组并将基于.png的纹理映射到它。您可以将四边形面向任何您想要的方向,如第一张图片,或使其始终面向相机(如Svenstaro所提到的广告牌),如第二张图片中所示。虽然,公平地说,我确信第二张图片只是在软件创建的帧缓冲区(看起来像Wolf3d技术,软件渲染)中直接显示图像(带有一些缩放)。

答案 4 :(得分:1)

看看OpenGL Point Sprites:

http://www.informit.com/articles/article.aspx?p=770639&seqNum=7

对于部分系统特别有用,但可以为您的目的提供技巧。

答案 5 :(得分:1)

查看本教程有关广告牌的信息。我觉得你会觉得很有用。 http://www.lighthouse3d.com/opengl/billboarding/

答案 6 :(得分:0)

opengl-tutorial 有:

截图:

enter image description here

代码:

#include <stdio.h>
#include <stdlib.h>

#include <vector>
#include <algorithm>

#include <GL/glew.h>

#include <glfw3.h>
GLFWwindow* window;

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/norm.hpp>
using namespace glm;


#include <common/shader.hpp>
#include <common/texture.hpp>
#include <common/controls.hpp>

#define DRAW_CUBE // Comment or uncomment this to simplify the code

int main( void )
{
    // Initialise GLFW
    if( !glfwInit() )
    {
        fprintf( stderr, "Failed to initialize GLFW\n" );
        getchar();
        return -1;
    }

    glfwWindowHint(GLFW_SAMPLES, 4);
    glfwWindowHint(GLFW_RESIZABLE,GL_FALSE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    // Open a window and create its OpenGL context
    window = glfwCreateWindow( 1024, 768, "Tutorial 18 - Billboards", NULL, NULL);
    if( window == NULL ){
        fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n" );
        getchar();
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);

    // Initialize GLEW
    glewExperimental = true; // Needed for core profile
    if (glewInit() != GLEW_OK) {
        fprintf(stderr, "Failed to initialize GLEW\n");
        getchar();
        glfwTerminate();
        return -1;
    }

    // Ensure we can capture the escape key being pressed below
    glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
    // Hide the mouse and enable unlimited mouvement
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // Set the mouse at the center of the screen
    glfwPollEvents();
    glfwSetCursorPos(window, 1024/2, 768/2);

    // Dark blue background
    glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

    // Enable depth test
    glEnable(GL_DEPTH_TEST);
    // Accept fragment if it closer to the camera than the former one
    glDepthFunc(GL_LESS);

    GLuint VertexArrayID;
    glGenVertexArrays(1, &VertexArrayID);
    glBindVertexArray(VertexArrayID);


    // Create and compile our GLSL program from the shaders
    GLuint programID = LoadShaders( "Billboard.vertexshader", "Billboard.fragmentshader" );

    // Vertex shader
    GLuint CameraRight_worldspace_ID  = glGetUniformLocation(programID, "CameraRight_worldspace");
    GLuint CameraUp_worldspace_ID  = glGetUniformLocation(programID, "CameraUp_worldspace");
    GLuint ViewProjMatrixID = glGetUniformLocation(programID, "VP");
    GLuint BillboardPosID = glGetUniformLocation(programID, "BillboardPos");
    GLuint BillboardSizeID = glGetUniformLocation(programID, "BillboardSize");
    GLuint LifeLevelID = glGetUniformLocation(programID, "LifeLevel");

    GLuint TextureID  = glGetUniformLocation(programID, "myTextureSampler");


    GLuint Texture = loadDDS("ExampleBillboard.DDS");

    // The VBO containing the 4 vertices of the particles.
    static const GLfloat g_vertex_buffer_data[] = { 
         -0.5f, -0.5f, 0.0f,
          0.5f, -0.5f, 0.0f,
         -0.5f,  0.5f, 0.0f,
          0.5f,  0.5f, 0.0f,
    };
    GLuint billboard_vertex_buffer;
    glGenBuffers(1, &billboard_vertex_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, billboard_vertex_buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_DYNAMIC_DRAW);

#ifdef DRAW_CUBE
    // Everything here comes from Tutorial 4
    GLuint cubeProgramID = LoadShaders( "../tutorial04_colored_cube/TransformVertexShader.vertexshader", "../tutorial04_colored_cube/ColorFragmentShader.fragmentshader" );
    GLuint cubeMatrixID = glGetUniformLocation(cubeProgramID, "MVP");
    static const GLfloat g_cube_vertex_buffer_data[] = { -1.0f,-1.0f,-1.0f,-1.0f,-1.0f, 1.0f,-1.0f, 1.0f, 1.0f,1.0f, 1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f, 1.0f,-1.0f,1.0f,-1.0f, 1.0f,-1.0f,-1.0f,-1.0f,1.0f,-1.0f,-1.0f,1.0f, 1.0f,-1.0f,1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f,1.0f,-1.0f, 1.0f,-1.0f,-1.0f, 1.0f,-1.0f,-1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f,1.0f,-1.0f, 1.0f,1.0f, 1.0f, 1.0f,1.0f,-1.0f,-1.0f,1.0f, 1.0f,-1.0f,1.0f,-1.0f,-1.0f,1.0f, 1.0f, 1.0f,1.0f,-1.0f, 1.0f,1.0f, 1.0f, 1.0f,1.0f, 1.0f,-1.0f,-1.0f, 1.0f,-1.0f,1.0f, 1.0f, 1.0f,-1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 1.0f,1.0f,-1.0f, 1.0f};
    static const GLfloat g_cube_color_buffer_data[] = { 0.583f,  0.771f,  0.014f,0.609f,  0.115f,  0.436f,0.327f,  0.483f,  0.844f,0.822f,  0.569f,  0.201f,0.435f,  0.602f,  0.223f,0.310f,  0.747f,  0.185f,0.597f,  0.770f,  0.761f,0.559f,  0.436f,  0.730f,0.359f,  0.583f,  0.152f,0.483f,  0.596f,  0.789f,0.559f,  0.861f,  0.639f,0.195f,  0.548f,  0.859f,0.014f,  0.184f,  0.576f,0.771f,  0.328f,  0.970f,0.406f,  0.615f,  0.116f,0.676f,  0.977f,  0.133f,0.971f,  0.572f,  0.833f,0.140f,  0.616f,  0.489f,0.997f,  0.513f,  0.064f,0.945f,  0.719f,  0.592f,0.543f,  0.021f,  0.978f,0.279f,  0.317f,  0.505f,0.167f,  0.620f,  0.077f,0.347f,  0.857f,  0.137f,0.055f,  0.953f,  0.042f,0.714f,  0.505f,  0.345f,0.783f,  0.290f,  0.734f,0.722f,  0.645f,  0.174f,0.302f,  0.455f,  0.848f,0.225f,  0.587f,  0.040f,0.517f,  0.713f,  0.338f,0.053f,  0.959f,  0.120f,0.393f,  0.621f,  0.362f,0.673f,  0.211f,  0.457f,0.820f,  0.883f,  0.371f,0.982f,  0.099f,  0.879f};
    GLuint cubevertexbuffer;
    glGenBuffers(1, &cubevertexbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, cubevertexbuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_cube_vertex_buffer_data), g_cube_vertex_buffer_data, GL_DYNAMIC_DRAW);
    GLuint cubecolorbuffer;
    glGenBuffers(1, &cubecolorbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, cubecolorbuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_cube_color_buffer_data), g_cube_color_buffer_data, GL_DYNAMIC_DRAW);
#endif

    double lastTime = glfwGetTime();
    do
    {
        // Clear the screen
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        double currentTime = glfwGetTime();
        double delta = currentTime - lastTime;
        lastTime = currentTime;


        computeMatricesFromInputs();
        glm::mat4 ProjectionMatrix = getProjectionMatrix();
        glm::mat4 ViewMatrix = getViewMatrix();



#ifdef DRAW_CUBE
        // Again : this is just Tutorial 4 !
        glDisable(GL_BLEND);
        glUseProgram(cubeProgramID);
        glm::mat4 cubeModelMatrix(1.0f);
        cubeModelMatrix = glm::scale(cubeModelMatrix, glm::vec3(0.2f, 0.2f, 0.2f));
        glm::mat4 cubeMVP = ProjectionMatrix * ViewMatrix * cubeModelMatrix;
        glUniformMatrix4fv(cubeMatrixID, 1, GL_FALSE, &cubeMVP[0][0]);
        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, cubevertexbuffer);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0  );
        glEnableVertexAttribArray(1);
        glBindBuffer(GL_ARRAY_BUFFER, cubecolorbuffer);
        glVertexAttribPointer(1, 3, GL_FLOAT,  GL_FALSE, 0, (void*)0   );
        glDrawArrays(GL_TRIANGLES, 0, 12*3);
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
#endif



        // We will need the camera's position in order to sort the particles
        // w.r.t the camera's distance.
        // There should be a getCameraPosition() function in common/controls.cpp, 
        // but this works too.
        glm::vec3 CameraPosition(glm::inverse(ViewMatrix)[3]);

        glm::mat4 ViewProjectionMatrix = ProjectionMatrix * ViewMatrix;





        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        // Use our shader
        glUseProgram(programID);

        // Bind our texture in Texture Unit 0
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, Texture);
        // Set our "myTextureSampler" sampler to user Texture Unit 0
        glUniform1i(TextureID, 0);

        // This is the only interesting part of the tutorial.
        // This is equivalent to mlutiplying (1,0,0) and (0,1,0) by inverse(ViewMatrix).
        // ViewMatrix is orthogonal (it was made this way), 
        // so its inverse is also its transpose, 
        // and transposing a matrix is "free" (inversing is slooow)
        glUniform3f(CameraRight_worldspace_ID, ViewMatrix[0][0], ViewMatrix[1][0], ViewMatrix[2][0]);
        glUniform3f(CameraUp_worldspace_ID   , ViewMatrix[0][1], ViewMatrix[1][1], ViewMatrix[2][1]);

        glUniform3f(BillboardPosID, 0.0f, 0.5f, 0.0f); // The billboard will be just above the cube
        glUniform2f(BillboardSizeID, 1.0f, 0.125f);     // and 1m*12cm, because it matches its 256*32 resolution =)

        // Generate some fake life level and send it to glsl
        float LifeLevel = sin(currentTime)*0.1f + 0.7f;
        glUniform1f(LifeLevelID, LifeLevel);

        glUniformMatrix4fv(ViewProjMatrixID, 1, GL_FALSE, &ViewProjectionMatrix[0][0]);

        // 1rst attribute buffer : vertices
        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, billboard_vertex_buffer);
        glVertexAttribPointer(
            0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
            3,                  // size
            GL_FLOAT,           // type
            GL_FALSE,           // normalized?
            0,                  // stride
            (void*)0            // array buffer offset
        );


        // Draw the billboard !
        // This draws a triangle_strip which looks like a quad.
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        glDisableVertexAttribArray(0);


        // Swap buffers
        glfwSwapBuffers(window);
        glfwPollEvents();

    } // Check if the ESC key was pressed or the window was closed
    while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
           glfwWindowShouldClose(window) == 0 );


    // Cleanup VBO and shader
    glDeleteBuffers(1, &billboard_vertex_buffer);
    glDeleteProgram(programID);
    glDeleteTextures(1, &TextureID);
    glDeleteVertexArrays(1, &VertexArrayID);
#ifdef DRAW_CUBE
    glDeleteProgram(cubeProgramID);
    glDeleteVertexArrays(1, &cubevertexbuffer);
    glDeleteVertexArrays(1, &cubecolorbuffer);
#endif
    // Close OpenGL window and terminate GLFW
    glfwTerminate();

    return 0;
}

在Ubuntu 15.10上测试。

这个问题的面向轴的版本:https://gamedev.stackexchange.com/questions/35946/how-do-i-implement-camera-axis-aligned-billboards我们在这里做了一个面向观点的广告牌。