c++ - 带有 ADS Phong "veiny-looking"球体的 OpenGL 3.x .obj 加载器

标签 c++ opengl sfml opengl-3 wavefront

我一直在尝试使用 OpenGL 3.x 核心配置文件制作我自己的基本 .obj(Wavefront)渲染器。我正在使用 OpenGL SuperBible 第 5 版。以及作为引用资料的 Swiftless 教程。

几何体似乎加载正确,所以现在我正在尝试让 ADS Phong 照明模型工作,但有些东西很奇怪,我认为这与我的 OpenGL 调用有关,或者可能与我的方式有关正在加载我的法线,但我似乎无法弄清楚如何修复它。 (我猜这也可能是其他问题,因为我不是 OpenGL 专家)。

当渲染一个简单的立方体时,它看起来几乎是正确的,但在一侧有一个奇怪的光点:

picture of cube

渲染球体时,光照显示为“脉络”:

picture of sphere

我的代码显然有问题。以下是我认为可能相关的代码部分。如有必要,我很乐意发布更多/所有代码...让我知道是否应该发布更多。

文件:objFileRenderer.cpp

#include "objFileRenderer.hpp"

namespace def
{

objFileRenderer::objFileRenderer(const char* fileToRender, float objScale)
{
    windowWidth = 800;
    windowHeight = 600;
    settings.majorVersion = 3;
    settings.minorVersion = 1;
    app = NULL;
    shader = NULL;
    vaoID = NULL;
    vboID = NULL;
    iboID = NULL;
    vaoID = new unsigned int[1];
    vboID = new unsigned int[3];
    iboID = new unsigned int[2];
    rotSpeed = 50.0f;
    rot = 0.0f;

    initSFML();
    initOpenGL();

    std::string objFilename(fileToRender);
    std::vector< float > vertices;
    std::vector< unsigned short > indices;
    std::vector< float > normals;
    std::vector< unsigned short > normalIndices;

    loadObj(objFilename, vertices, indices, normals, normalIndices);


    std::vector< float > colorA;
    std::vector< float > colorD;
    std::vector< float > colorS;
    loadMtl(objFilename, colorA, colorD, colorS);


    float* vertexArray = NULL;
    int numVertices = 0;
    unsigned short* indexArray = NULL;
    int numFaces = 0;
    vertexArray = vertexVectorToVertexArray(vertices, numVertices);
    indexArray = indexVectorToIndexArray(indices, numFaces);

    float* colorArrayA = NULL;
    float* colorArrayD = NULL;
    float* colorArrayS = NULL;
    int numColoredObjects = 0;
    colorVectorsToColorArrays(colorA, colorD, colorS, colorArrayA, colorArrayD, colorArrayS, numColoredObjects);

    float* normalArray = NULL;
    unsigned short* normalIndicesArray = NULL;
    int numNormals = 0;
    int numNormalIndices = 0;
    normalVectorsToNormalArrays(normals, normalIndices, normalArray, normalIndicesArray, numNormals, numNormalIndices);

    setupScene();

    putArraysIntoVAO(vertexArray, numVertices, indexArray, numFaces, normalArray, numNormals, normalIndicesArray, numNormalIndices);

    mainLoop(numVertices, numFaces, colorArrayA, colorArrayD, colorArrayD, normalArray, objScale);

    delete [] vertexArray;
    delete [] indexArray;
    delete [] colorArrayA;
    delete [] colorArrayD;
    delete [] colorArrayS;
    delete [] normalArray;
    delete [] normalIndicesArray;
}

objFileRenderer::~objFileRenderer()
{
    shutdownSFML();
}

void objFileRenderer::loadObj(std::string& objFilename, std::vector< float >& vertices, std::vector< unsigned short >& indices, std::vector< float >& normals, std::vector< unsigned short >& normalIndices)
{
    std::ifstream objFile(objFilename.c_str());

    if (!objFile.is_open())
    {
        std::cerr << "Error: unable to open .obj file: " << objFilename << std::endl;
        exit(1);
    }

    std::string line;
    while (objFile.good())
    {
        getline(objFile, line);

        // vertices
        if (line[0] == 'v' && line[1] == ' ') // if line in .obj file contains vertices
        {
            std::vector< std::string > tmpStrVerts;
            std::string subline;
            subline = line.substr(2);

            boost::split(tmpStrVerts, subline, boost::is_any_of("\t "));

            std::vector< std::string >::iterator it;
            for (it = tmpStrVerts.begin(); it != tmpStrVerts.end(); it++)
            {
                float vertex;
                std::stringstream ss;
                ss << *it;
                ss >> vertex;
                vertices.push_back(vertex);
            }
        }

        // normals
        else if (line[0] == 'v' && line[1] == 'n')
        {
            std::vector< std::string > tmpStrNorms;
            std::string subline;
            subline = line.substr(3);

            boost::split(tmpStrNorms, subline, boost::is_any_of("\t "));

            std::vector< std::string >::iterator it;
            for (it = tmpStrNorms.begin(); it != tmpStrNorms.end(); it++)
            {
                float normal;
                std::stringstream ss;
                ss << *it;
                ss >> normal;
                normals.push_back(normal);
                //std::cout << normal << std::endl;
            }
        }

        // indices and normalIndices
        else if (line[0] == 'f' && line[1] == ' ') // else if line in .obj file contains indices
        {
            std::vector< std::string > tmpStrIndices;
            std::string subline;
            subline = line.substr(2);

            // indices
            boost::split(tmpStrIndices, subline, boost::is_any_of("\t "));

            std::vector< std::string >::iterator it;
            for (it = tmpStrIndices.begin(); it != tmpStrIndices.end(); it++)
            {
                unsigned short index;
                std::stringstream ss;
                ss << *it;
                ss >> index;
                indices.push_back(index);
            }

            // normalIndices
            boost::split(tmpStrIndices, subline, boost::is_any_of("/"));

            int count = 0;
            std::vector< std::string >::iterator it2;
            for (it2 = tmpStrIndices.begin(); it2 != tmpStrIndices.end(); it2++)
            {
                if (count == 2)
                {
                    unsigned short index;
                    std::stringstream ss;
                    ss << *it2;
                    ss >> index;
                    normalIndices.push_back(index);
                    count = 0;
                }
                count++;
            }


        }
    }
    objFile.close();

    return;
}

void objFileRenderer::loadMtl(std::string& objFilename, std::vector< float >& colorA, std::vector< float >& colorD, std::vector< float >& colorS)
{
    int extpos = objFilename.find('.');
    std::string mtlFilename = objFilename.substr(0, extpos+1) + "mtl";

    std::ifstream mtlFile(mtlFilename.c_str());

    if (!mtlFile.is_open())
    {
        std::cerr << "Error: unable to open .mtl file: " << mtlFilename << std::endl;
        exit(1);
    }

    std::string line;
    while (mtlFile.good())
    {
        getline(mtlFile, line);

        if (line[0] == 'K' && line[1] == 'a')
        {
            std::vector< std::string > tmpStrColorA;
            std::string subline;
            subline = line.substr(3);

            boost::split(tmpStrColorA, subline, boost::is_any_of("\t "));

            std::vector< std::string >::iterator it;
            for (it = tmpStrColorA.begin(); it != tmpStrColorA.end(); it++)
            {
                float rgbValue;
                std::stringstream ss;
                ss << *it;
                ss >> rgbValue;
                colorA.push_back(rgbValue);
            }
        }

        if (line[0] == 'K' && line[1] == 'd')
        {
            std::vector< std::string > tmpStrColorD;
            std::string subline;
            subline = line.substr(3);

            boost::split(tmpStrColorD, subline, boost::is_any_of("\t "));

            std::vector< std::string >::iterator it;
            for (it = tmpStrColorD.begin(); it != tmpStrColorD.end(); it++)
            {
                float rgbValue;
                std::stringstream ss;
                ss << *it;
                ss >> rgbValue;
                colorD.push_back(rgbValue);
            }
        }

        if (line[0] == 'K' && line[1] == 's')
        {
            std::vector< std::string > tmpStrColorS;
            std::string subline;
            subline = line.substr(3);

            boost::split(tmpStrColorS, subline, boost::is_any_of("\t "));

            std::vector< std::string >::iterator it;
            for (it = tmpStrColorS.begin(); it != tmpStrColorS.end(); it++)
            {
                float rgbValue;
                std::stringstream ss;
                ss << *it;
                ss >> rgbValue;
                colorS.push_back(rgbValue);
            }
        }

    }
    mtlFile.close();

    return;
}

float* objFileRenderer::vertexVectorToVertexArray(std::vector< float >& vertices, int& numVertices)
{
    numVertices = vertices.size() / 3;
    float* vertexArray = NULL;
    vertexArray = new float[vertices.size()];

    for (unsigned int i = 0; i < vertices.size(); i++)
    {
        vertexArray[i] = vertices[i];
    }

    return vertexArray;
}

unsigned short* objFileRenderer::indexVectorToIndexArray(std::vector< unsigned short >& indices, int& numFaces)
{
    numFaces = indices.size() / 3;
    unsigned short* indexArray = NULL;
    indexArray = new unsigned short[indices.size()];

    for (unsigned int i = 0; i < indices.size(); i++)
    {
        indexArray[i] = indices[i]-1;
    }

    return indexArray;
}

void objFileRenderer::colorVectorsToColorArrays(std::vector< float >& colorA, std::vector< float >& colorD, std::vector< float >& colorS, float*& colorArrayA, float*& colorArrayD, float*& colorArrayS, int& numColoredObjects)
{
    numColoredObjects = colorA.size() / 3;

    colorArrayA = new float[numColoredObjects*3];
    colorArrayD = new float[numColoredObjects*3];
    colorArrayS = new float[numColoredObjects*3];

    for (int i = 0; i < numColoredObjects; i+=3)
    {
        colorArrayA[i] = colorA[i]; colorArrayA[i+1] = colorA[i+1]; colorArrayA[i+2] = colorA[i+2];
        colorArrayD[i] = colorD[i]; colorArrayD[i+1] = colorD[i+1]; colorArrayD[i+2] = colorD[i+2];
        colorArrayS[i] = colorS[i]; colorArrayS[i+1] = colorS[i+1]; colorArrayS[i+2] = colorS[i+2];
    }

    return;
}

void objFileRenderer::normalVectorsToNormalArrays(std::vector< float >& normals, std::vector< unsigned short >& normalIndices, float*& normalArray, unsigned short*& normalIndicesArray, int& numNormals, int& numNormalIndices)
{
    numNormals = normals.size() / 3;
    numNormalIndices = normalIndices.size();

    normalArray = new float[numNormalIndices];
    normalIndicesArray = new unsigned short[numNormalIndices];

    for (int i = 0; i < numNormalIndices; i+=3)
    {
        normalIndicesArray[i] = normalIndices[i]-1;
        normalIndicesArray[i+1] = normalIndices[i+1]-1;
        normalIndicesArray[i+2] = normalIndices[i+2]-1;
    }

    // load normals in index order
    for (int i = 0; i < numNormalIndices; i+=3)
    {
        int index = normalIndicesArray[i];
        normalArray[i] = normals[index];
        normalArray[i+1] = normals[index+1];
        normalArray[i+2] = normals[index+2];
    }

    return;
}

void objFileRenderer::putArraysIntoVAO(float* vertexArray, int& numVertices, unsigned short* indexArray, int& numFaces, float* normalArray, int& numNormals, unsigned short* normalIndicesArray, int& numNormalIndices)
{
    glGenVertexArrays(1, &vaoID[0]); // create our vertex array object
    glBindVertexArray(vaoID[0]); // bind our vertex array object so we can use it

    glGenBuffers(2, &iboID[0]); // generate our index buffer object
    glGenBuffers(2, &vboID[0]); // generate our vertex buffer object

//  normalArray holds normals in index order, so I shouldn't use this
//  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID[1]); // bind our normal index buffer object
//  glBufferData(GL_ELEMENT_ARRAY_BUFFER, (numNormalIndices) * sizeof(GLushort), normalIndicesArray, GL_STATIC_DRAW); // set the size and data of our IBO

    glBindBuffer(GL_ARRAY_BUFFER, vboID[1]); // bind our normal vertex buffer object
    glBufferData(GL_ARRAY_BUFFER, (numNormalIndices) * sizeof(GLfloat), normalArray, GL_STATIC_DRAW); // set the size and data of our VBO and set it to STATIC_DRAW
    glVertexAttribPointer((GLuint)1, 3, GL_FLOAT, GL_FALSE, 0, 0); // set up our vertex attributes pointer
    glEnableVertexAttribArray(1);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID[0]); // bind our index buffer object
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, (numFaces*3) * sizeof(GLushort), indexArray, GL_STATIC_DRAW); // set the size and data of our IBO

    glBindBuffer(GL_ARRAY_BUFFER, vboID[0]); // bind our vertex buffer object
    glBufferData(GL_ARRAY_BUFFER, (numVertices*3) * sizeof(GLfloat), vertexArray, GL_STATIC_DRAW); // set the size and data of our VBO and set it to STATIC_DRAW
    glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0); // set up our vertex attributes pointer
    glEnableVertexAttribArray(0);

    glBindVertexArray(0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    return;
}

void objFileRenderer::setupScene()
{
    app->setFramerateLimit(60); // max 60 FPS

    glClearColor(0.4f, 0.6f, 0.9f, 0.0f);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_CULL_FACE);

    shader = new Shader("shader.vert", "shader.frag");
    projectionMatrix = glm::perspective(60.0f, (float)windowWidth / (float)windowHeight, 0.1f, 100.0f);

    return;
}

void objFileRenderer::renderScene(int& numVertices, int& numFaces, float*& colorArrayA, float*& colorArrayD, float*& colorArrayS, float*& normalArray, float objScale)
{
    sf::Time elapsedTime = clock.getElapsedTime();
    clock.restart();

    if (rot > 360.0f)
        rot = 0.0f;
    rot += rotSpeed * elapsedTime.asSeconds();

    float lightPosition[3] = { -100.0, -100.0, 100.0 };

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -3.0f, -10.0f)); // move back by 5 units
    modelMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(objScale)); // change last arg to 0.5f to shrink model by half
    modelMatrix *= glm::rotate< float >(glm::mat4(1.0f), rot, glm::vec3(0, 1, 0));

    shader->bind();

    int projectionMatrixLocation    = glGetUniformLocation(shader->id(), "projectionMatrix");
    int viewMatrixLocation          = glGetUniformLocation(shader->id(), "viewMatrix");
    int modelMatrixLocation         = glGetUniformLocation(shader->id(), "modelMatrix");
    int ambientLocation             = glGetUniformLocation(shader->id(), "ambientColor");
    int diffuseLocation             = glGetUniformLocation(shader->id(), "diffuseColor");
    int specularLocation            = glGetUniformLocation(shader->id(), "specularColor");
    int lightPositionLocation       = glGetUniformLocation(shader->id(), "lightPosition");
    int normalMatrixLocation        = glGetUniformLocation(shader->id(), "normalMatrix");

    glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, &projectionMatrix[0][0]);
    glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, &viewMatrix[0][0]);
    glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &modelMatrix[0][0]);
    glUniform3fv(ambientLocation, 1, colorArrayA);
    glUniform3fv(diffuseLocation, 1, colorArrayD);
    glUniform3fv(specularLocation, 1, colorArrayS);
    glUniform3fv(lightPositionLocation, 1, lightPosition);
    glUniformMatrix3fv(normalMatrixLocation, 1, GL_FALSE, normalArray);


    glBindVertexArray(vaoID[0]);

    glDrawRangeElements(GL_TRIANGLES, 0, numFaces*3, numFaces*3, GL_UNSIGNED_SHORT, NULL);

    glBindVertexArray(0);

    shader->unbind();

    app->display();

    return;
}

void objFileRenderer::handleEvents()
{
    sf::Event event;

    while (app->pollEvent(event))
    {
        if (event.type == sf::Event::Closed)
        {
            app->close();
        }

        if ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape))
        {
            app->close();
        }

        if (event.type == sf::Event::Resized)
        {
            glViewport(0, 0, event.size.width, event.size.height);
        }
    }

    return;
}

void objFileRenderer::mainLoop(int& numVertices, int& numFaces, float*& colorArrayA, float*& colorArrayD, float*& colorArrayS, float*& normalArray, float objScale)
{
    while (app->isOpen())
    {
        renderScene(numVertices, numFaces, colorArrayA, colorArrayD, colorArrayS, normalArray, objScale);
        handleEvents();
    }
}


}

文件:shader.cpp

#include "shader.h"
#include <string.h>
#include <iostream>
#include <stdlib.h>

using namespace std;

static char* textFileRead(const char *fileName) {
    char* text = NULL;

    if (fileName != NULL) {
        FILE *file = fopen(fileName, "rt");

        if (file != NULL) {
            fseek(file, 0, SEEK_END);
            int count = ftell(file);
            rewind(file);

            if (count > 0) {
                text = (char*)malloc(sizeof(char) * (count + 1));
                count = fread(text, sizeof(char), count, file);
                text[count] = '\0';
            }
            fclose(file);
        }
    }
    return text;
}

Shader::Shader() {

}

Shader::Shader(const char *vsFile, const char *fsFile) {
    init(vsFile, fsFile);
}

void Shader::init(const char *vsFile, const char *fsFile) {
    shader_vp = glCreateShader(GL_VERTEX_SHADER);
    shader_fp = glCreateShader(GL_FRAGMENT_SHADER);

    const char* vsText = NULL;
    const char* fsText = NULL;
    vsText = textFileRead(vsFile);
    fsText = textFileRead(fsFile);

    if (vsText == NULL)
    {
        cerr << "Error: vertex shader file not found" << endl;
    }
    if (fsText == NULL) {
        cerr << "Error: fragment shader file not found." << endl;
    }
    if (vsText == NULL || fsText == NULL)
        return;

    glShaderSource(shader_vp, 1, &vsText, 0);
    glShaderSource(shader_fp, 1, &fsText, 0);

    glCompileShader(shader_vp);
    glCompileShader(shader_fp);

    shader_id = glCreateProgram();
    glAttachShader(shader_id, shader_fp);
    glAttachShader(shader_id, shader_vp);
    glLinkProgram(shader_id);

    glBindAttribLocation(shader_id, 0, "in_Position");
    //glBindAttribLocation(shader_id, 1, "in_Color");
    glBindAttribLocation(shader_id, 1, "in_Normal");
}

Shader::~Shader() {
    glDetachShader(shader_id, shader_fp);
    glDetachShader(shader_id, shader_vp);

    glDeleteShader(shader_fp);
    glDeleteShader(shader_vp);
    glDeleteProgram(shader_id);
}

unsigned int Shader::id() {
    return shader_id;
}

void Shader::bind() {
    glUseProgram(shader_id);
}

void Shader::unbind() {
    glUseProgram(0);
}

文件:shader.vert

#version 150 core

in vec3 in_Position;
in vec3 in_Normal;

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
uniform vec3 lightPosition;
uniform mat3 normalMatrix;

smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;

void main()
{       
    // derive MVP and MV matrices
    mat4 modelViewProjectionMatrix = projectionMatrix * viewMatrix * modelMatrix;
    mat4 modelViewMatrix = viewMatrix * modelMatrix;

    // get surface normal in eye coordinates
    vVaryingNormal = normalMatrix * in_Normal;

    // get vertex position in eye coordinates
    vec4 vPosition4 = modelViewMatrix * vec4(in_Position, 1.0);
    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;

    // get vector to light source
    vVaryingLightDir = normalize(lightPosition - vPosition3);


    // Set the position of the current vertex 
    gl_Position = modelViewProjectionMatrix * vec4(in_Position, 1.0);

}

文件:shader.frag

#version 150 core

out vec4 out_Color;

uniform vec3 ambientColor;
uniform vec3 diffuseColor;
uniform vec3 specularColor;

smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;

void main()
{
    // dot product gives us diffuse intensity
    float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir)));

    // multiply intensity by diffuse color, force alpha to 1.0
    out_Color = vec4(diff * diffuseColor, 1.0);

    // add in ambient light
    out_Color += vec4(ambientColor, 1.0);

    // specular light
    vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir), normalize(vVaryingNormal)));
    float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection));

    if (diff != 0)
    {
        float fSpec = pow(spec, 128.0);
        // Set the output color of our current pixel
        out_Color.rgb += vec3(fSpec, fSpec, fSpec);
    }
}

我知道要查看的内容很多,但我很乐意得到一些帮助来查明这个问题的根源,所以提前感谢任何有时间帮助我解决这个问题的人!

最佳答案

我认为您没有正确处理索引。 OBJ 在 opengl 中很难解析,因为它们使用无用的多索引格式提供数据。本质上,您必须分解所有顶点和法线并重建索引,以便对于每个顶点,它的位置/法线/颜色/无论什么都共享相同的索引。重新排序它们并不是一件容易的事,但如果您在线查找“obj vertex buffer”,我相信您会找到数百个关于它的引用和帖子。

试着阅读这篇文章,看看它是否更有意义:http://aresio.blogspot.com/2009/07/wavefront-obj-files-vertex-buffer.html

关于c++ - 带有 ADS Phong "veiny-looking"球体的 OpenGL 3.x .obj 加载器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9898075/

相关文章:

c++ - c中无符号整数的类型是什么?

c++ - 构造一个内部有结构的类 C++

c++ - 绑定(bind)的 VAO 问题

iphone - 如何在没有 .nib 文件的情况下创建 opengl iPhone 应用程序?

c++ - 使用索引缓冲区对象时出现奇怪的段错误

c++ - 插入 std::map 的最佳方式

c++ - 函数没有返回正确的结果

c++ - C++ OpenGL 中定位光和直射光的区别?

c++ - 当您首先绘制具有相似纹理的 Sprite 时,sf::RenderWindow 是否绘制得更快

c++ - SFML 的 'intersects' 函数总是返回 'true'