我一直在尝试使用OpenGL 3.x核心配置文件制作我自己的基本.obj(Wavefront)渲染器。我正在使用OpenGL SuperBible第5版。和Swiftless教程作为参考资料。
几何体似乎正确加载,所以现在我试图让ADS Phong照明模型起作用,但有些事情很棘手,我认为它与我的OpenGL调用有关,或者也许就像我的方式加载我的法线,但我似乎无法弄清楚如何解决它。 (我猜也可能是其他问题,因为我不是OpenGL的专家)。
渲染一个简单的立方体时,它几乎看起来正确,但一边有一个奇怪的光点:
渲染球体时,灯光会显示“静脉”:
我的代码显然有些问题。以下是我认为可能相关的代码部分。如果有必要,我会很乐意发布更多/所有代码...让我知道我是否应该发布更多代码。
file: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();
}
}
}
file: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);
}
file: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);
}
file: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);
}
}
我知道这很重要,但我很乐意帮助解决这个问题的根源,所以提前感谢任何有时间帮助我解决这个问题的人!
答案 0 :(得分:2)
我认为你没有正确对待指数。 OBJ在opengl中解析很棘手,因为它们使用无用的多索引格式提供数据。基本上你必须分解所有的顶点和法线并重建索引,这样对于每个顶点,它的位置/正常/颜色/任何都共享相同的索引。重新排序它们并不是一项微不足道的任务,但是如果你在线查看“obj顶点缓冲区”,我相信你会发现数以百计的关于它的参考和帖子。
尝试阅读此内容,看看它是否更有意义:http://aresio.blogspot.com/2009/07/wavefront-obj-files-vertex-buffer.html