c++ - OpenGL 3.x Assimp 实现 phong 着色(法线?)

标签 c++ opengl sfml phong

我无法使 phong 着色看起来正确。我很确定我的 OpenGL 调用或我加载法线的方式有问题,但我想这可能是其他问题,因为 3D 图形和 Assimp 对我来说都还很陌生。尝试加载 .obj/.mtl 文件时,我遇到的问题是:

  1. 模型的光线似乎过于强烈(phong 风格较少,完全褪色,太亮)。
  2. 被照亮的面部似乎全身都被均匀地照亮(除了只有当光源位置移动到模型的正上方时才会显示镜面高光)
  3. 由于问题 1 和 2,球体看起来很不对:

picture of sphere

而且脸大的东西看起来(不太明显)也是错误的:

picture of cube

我可能是错的,但对我来说这看起来不像是正确的 phong 着色。

这是我认为可能相关的代码(如有必要,我可以发布更多):

文件:assimpRenderer.cpp

#include "assimpRenderer.hpp"

namespace def
{

assimpRenderer::assimpRenderer(std::string modelFilename, float modelScale)
{
    initSFML();
    initOpenGL();

    if (assImport(modelFilename)) // if modelFile loaded successfully
    {
        initScene();
        mainLoop(modelScale);
        shutdownScene();
    }

    shutdownOpenGL();
    shutdownSFML();
}

assimpRenderer::~assimpRenderer()
{

}

void assimpRenderer::initSFML()
{
    windowWidth = 800;
    windowHeight = 600;
    settings.majorVersion = 3;
    settings.minorVersion = 3;
    app = NULL;
    shader = NULL;

    app = new sf::Window(sf::VideoMode(windowWidth,windowHeight,32), "OpenGL 3.x Window", sf::Style::Default, settings);
    app->setFramerateLimit(240);
    app->setActive();
    return;
}

void assimpRenderer::shutdownSFML()
{
    delete app;

    return;
}

void assimpRenderer::initOpenGL()
{
    GLenum err = glewInit();
    if (GLEW_OK != err)
    {
      /* Problem: glewInit failed, something is seriously wrong. */
      std::cerr << "Error: " << glewGetErrorString(err) << std::endl;
    }

    // check the OpenGL context version that's currently in use
    int glVersion[2] = {-1, -1};
    glGetIntegerv(GL_MAJOR_VERSION, &glVersion[0]); // get the OpenGL Major version
    glGetIntegerv(GL_MINOR_VERSION, &glVersion[1]); // get the OpenGL Minor version
    std::cout << "Using OpenGL Version: " << glVersion[0] << "." << glVersion[1] << std::endl;

    return;
}

void assimpRenderer::shutdownOpenGL()
{

    return;
}

void assimpRenderer::initScene()
{
    // allocate heap space for VAOs, VBOs, and IBOs
    vaoID = new GLuint[scene->mNumMeshes];
    vboID = new GLuint[scene->mNumMeshes*2];
    iboID = new GLuint[scene->mNumMeshes];

    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);

    rot = 0.0f;
    rotSpeed = 50.0f;

    faceIndex = 0;


    colorArrayA = NULL;
    colorArrayD = NULL;
    colorArrayS = NULL;

    normalArray = NULL;


    genVAOs();

    return;
}

void assimpRenderer::shutdownScene()
{
    delete [] iboID;
    delete [] vboID;
    delete [] vaoID;

    delete shader;
}

void assimpRenderer::renderScene(float modelScale)
{
    sf::Time elapsedTime = clock.getElapsedTime();
    clock.restart();

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

    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 a bit
    modelMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(modelScale));   // scale model
    modelMatrix = glm::rotate(modelMatrix, rot, glm::vec3(0, 1, 0));
    //modelMatrix = glm::rotate(modelMatrix, 25.0f, glm::vec3(0, 1, 0));


    glm::vec3 lightPosition( 0.0f, -100.0f, 0.0f );

    float lightPositionArray[3];
    lightPositionArray[0] = lightPosition[0];
    lightPositionArray[1] = lightPosition[1];
    lightPositionArray[2] = lightPosition[2];


    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(lightPositionLocation, 1, lightPositionArray);

    for (unsigned int i = 0; i < scene->mNumMeshes; i++)
    {
        colorArrayA = new float[3];
        colorArrayD = new float[3];
        colorArrayS = new float[3];

        material = scene->mMaterials[scene->mNumMaterials-1];

        normalArray = new float[scene->mMeshes[i]->mNumVertices * 3];

        unsigned int normalIndex = 0;
        for (unsigned int j = 0; j < scene->mMeshes[i]->mNumVertices * 3; j+=3, normalIndex++)
        {
            normalArray[j] = scene->mMeshes[i]->mNormals[normalIndex].x; // x
            normalArray[j+1] = scene->mMeshes[i]->mNormals[normalIndex].y; // y
            normalArray[j+2] = scene->mMeshes[i]->mNormals[normalIndex].z; // z
        }
        normalIndex = 0;


        glUniformMatrix3fv(normalMatrixLocation, 1, GL_FALSE, normalArray);

        aiColor3D ambient(0.0f, 0.0f, 0.0f);
        material->Get(AI_MATKEY_COLOR_AMBIENT, ambient);

        aiColor3D diffuse(0.0f, 0.0f, 0.0f);
        material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse);

        aiColor3D specular(0.0f, 0.0f, 0.0f);
        material->Get(AI_MATKEY_COLOR_SPECULAR, specular);


        colorArrayA[0] = ambient.r; colorArrayA[1] = ambient.g; colorArrayA[2] = ambient.b;
        colorArrayD[0] = diffuse.r; colorArrayD[1] = diffuse.g; colorArrayD[2] = diffuse.b;
        colorArrayS[0] = specular.r; colorArrayS[1] = specular.g; colorArrayS[2] = specular.b;

        // bind color for each mesh
        glUniform3fv(ambientLocation, 1, colorArrayA);
        glUniform3fv(diffuseLocation, 1, colorArrayD);
        glUniform3fv(specularLocation, 1, colorArrayS);

        // render all meshes
        glBindVertexArray(vaoID[i]); // bind our VAO
        glDrawElements(GL_TRIANGLES, scene->mMeshes[i]->mNumFaces*3, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0); // unbind our VAO


        delete [] normalArray;

        delete [] colorArrayA;
        delete [] colorArrayD;
        delete [] colorArrayS;
    }

    shader->unbind();

    app->display();

    return;
}

void assimpRenderer::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 assimpRenderer::mainLoop(float modelScale)
{
    while (app->isOpen())
    {
        renderScene(modelScale);
        handleEvents();
    }
}

bool assimpRenderer::assImport(const std::string& pFile)
{
    // read the file with some example postprocessing
    scene = importer.ReadFile(pFile,
            aiProcess_CalcTangentSpace      |
            aiProcess_Triangulate           |
            aiProcess_JoinIdenticalVertices |
            aiProcess_SortByPType);

    // if the import failed, report it
    if (!scene)
    {
        std::cerr << "Error: " << importer.GetErrorString() << std::endl;
        return false;
    }

    return true;
}

void assimpRenderer::genVAOs()
{

    int vboIndex = 0;
    for (unsigned int i = 0; i < scene->mNumMeshes; i++, vboIndex+=2)
    {
        mesh = scene->mMeshes[i];
        indexArray = new unsigned int[mesh->mNumFaces * sizeof(unsigned int) * 3];

        // convert assimp faces format to array
        faceIndex = 0;

        for (unsigned int t = 0; t < mesh->mNumFaces; ++t)
        {
            const struct aiFace* face = &mesh->mFaces[t];
            std::memcpy(&indexArray[faceIndex], face->mIndices, sizeof(float) * 3);
            faceIndex += 3;
        }

        // generate VAO
        glGenVertexArrays(1, &vaoID[i]);
        glBindVertexArray(vaoID[i]);

        // generate IBO for faces
        glGenBuffers(1, &iboID[i]);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID[i]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * mesh->mNumFaces * 3, indexArray, GL_STATIC_DRAW);

        // generate VBO for vertices
        if (mesh->HasPositions())
        {
            glGenBuffers(1, &vboID[vboIndex]);
            glBindBuffer(GL_ARRAY_BUFFER, vboID[vboIndex]);
            glBufferData(GL_ARRAY_BUFFER, mesh->mNumVertices * sizeof(GLfloat) * 3, mesh->mVertices, GL_STATIC_DRAW);
            glEnableVertexAttribArray((GLuint)0);
            glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);
        }

        // generate VBO for normals
        if (mesh->HasNormals())
        {
            normalArray = new float[scene->mMeshes[i]->mNumVertices * 3];

            unsigned int normalIndex = 0;
            for (unsigned int j = 0; j < scene->mMeshes[i]->mNumVertices * 3; j+=3, normalIndex++)
            {
                normalArray[j] = scene->mMeshes[i]->mNormals[normalIndex].x; // x
                normalArray[j+1] = scene->mMeshes[i]->mNormals[normalIndex].y; // y
                normalArray[j+2] = scene->mMeshes[i]->mNormals[normalIndex].z; // z
            }
            normalIndex = 0;

            glGenBuffers(1, &vboID[vboIndex+1]);
            glBindBuffer(GL_ARRAY_BUFFER, vboID[vboIndex+1]);
            glBufferData(GL_ARRAY_BUFFER, mesh->mNumVertices * sizeof(GLfloat) * 3, normalArray, GL_STATIC_DRAW);
            glEnableVertexAttribArray((GLuint)1);
            glVertexAttribPointer((GLuint)1, 3, GL_FLOAT, GL_FALSE, 0, 0);

            delete [] normalArray;
        }

        // tex coord stuff goes here

        // unbind buffers
        glBindVertexArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

        delete [] indexArray;
    }
    vboIndex = 0;

    return;
}

}

文件: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);
    }
}

我知道要查看的内容很多,但我将大部分代码放在上面,以免假设问题出在哪里。

最佳答案

您对法线矩阵的处理是否正确?这对我来说看起来很奇怪。

    for (unsigned int j = 0; j < scene->mMeshes[i]->mNumVertices * 3; j+=3, normalIndex++)
    {
        normalArray[j] = scene->mMeshes[i]->mNormals[normalIndex].x; // x
        normalArray[j+1] = scene->mMeshes[i]->mNormals[normalIndex].y; // y
        normalArray[j+2] = scene->mMeshes[i]->mNormals[normalIndex].z; // z
    }
    glUniformMatrix3fv(normalMatrixLocation, 1, GL_FALSE, normalArray);

为什么normalMatrix和网格的顶点有关系?它应该与您的 modelMatrix 相同(前提是您没有进行任何非均匀缩放)。

关于c++ - OpenGL 3.x Assimp 实现 phong 着色(法线?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9985207/

相关文章:

c++ - map 编辑器性能建议

c++ - 我的 sfml 纹理在哪里超出范围?

python - 如何在不终止解释器的情况下中断 native 扩展代码?

c++ - std::vector::const_iterator 未指向所需数据

c++ - 不允许第二个 setuid

c - 在 OpenGL 中使用箭头键移动一些形状 - 它会缩小而不是移动

c++ - OpenGL翻译glm右侧还是左侧?

c++ - 绘制俄罗斯方 block (俄罗斯方 block )

c++ - 牡丹 AES-256,32 位 InitialVector

JAVA/Jogl/OpenGL 显示存储在字节数组中的 10 位图像数据