代码主要由Torus.hpp、Torus.cpp、Utils.hpp构成,通过加载纹理图片和着色器文件Torus_Texture_frag.glsl、Torus_Texture_vert.glsl实现画出一个甜甜圈的效果。
主要用到的图形库有glew、glfw3、soil2、glm
实现如下:
Utils.hpp包含了加载纹理和glsl着色器文件的功能性程序
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "SOIL2/SOIL2.h"
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp> // glm::value_ptr
#include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective
using namespace std;
class Utils
{
private:
static GLuint prepareShader(int shaderTYPE, const char* shaderPath);
public:
Utils(){};
static GLuint createShaderProgram(const char* vp, const char* fp);
static GLuint loadTexture(const char *texImagePath)
};
GLuint Utils::prepareShader(int shaderTYPE, const char* shaderPath)
{
GLint shaderCompiled;
string shaderStr = readShaderFile(shaderPath);
const char* shaderSrc = shaderStr.c_str();
GLuint shaderRef = glCreateShader(shaderTYPE);
glShaderSource(shaderRef, 1, &shaderSrc, NULL);
glCompileShader(shaderRef);
checkOpenGLError();
glGetShaderiv(shaderRef, GL_COMPILE_STATUS, &shaderCompiled);
if (shaderCompiled != 1)
{
if (shaderTYPE == 35633) cout << "Vertex ";
if (shaderTYPE == 36488) cout << "Tess Control ";
if (shaderTYPE == 36487) cout << "Tess Eval ";
if (shaderTYPE == 36313) cout << "Geometry ";
if (shaderTYPE == 35632) cout << "Fragment ";
cout << "shader compilation error." << endl;
printShaderLog(shaderRef);
}
return shaderRef;
}
GLuint Utils::createShaderProgram(const char* vp, const char* fp) {
GLuint vShader = prepareShader(GL_VERTEX_SHADER, vp);
GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fp);
GLuint vfprogram = glCreateProgram();
glAttachShader(vfprogram, vShader);
glAttachShader(vfprogram, fShader);
finalizeShaderProgram(vfprogram);
return vfprogram;
}
GLuint Utils::loadTexture(const char *texImagePath)
{
GLuint textureRef;
textureRef = SOIL_load_OGL_texture(texImagePath, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
if (textureRef == 0) cout << "didnt find texture file " << texImagePath << endl;
// ----- mipmap/anisotropic section
glBindTexture(GL_TEXTURE_2D, textureRef);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) {
GLfloat anisoset = 0.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisoset);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoset);
}
// ----- end of mipmap/anisotropic section
return textureRef;
}
主程序入口draw_Torus.cpp
#define GLEW_STATIC
#include<GL/glew.h>
#include<GLFW\glfw3.h>
#include<string>
#include<iostream>
#include<fstream>
#include<cmath>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include"SOIL2/SOIL2.h"
#include "Utils.hpp"
#include"Torus.hpp"
using namespace std;
#define numVAOs 1
#define numVBOs 4
float cameraX, cameraY, cameraZ;
float pyrLocX, pyrLocY, pyrLocZ;
GLuint renderingProgram;
GLuint vao[numVAOs];
GLuint vbo[numVBOs];
//allocate variables used in display() function, so that they won't need to be allocated during rendering
GLuint mvLoc, projLoc;
GLuint brickTexture;
int width, height;
float aspect, tf;
glm::mat4 pMat, vMat, mMat, mvMat;
Torus myTorus(1.8f, 1.0f, 48);
vector<int> ind;
void setupVertices(void) {
ind = myTorus.getIndices();
vector<glm::vec3> vert = myTorus.getVertices();
vector<glm::vec2> tex = myTorus.getTexCoords();
vector<glm::vec3> norm =myTorus.getNormals();
vector<float> pvalues;
vector<float> tvalues;
vector<float> nvalues;
int numVertices = myTorus.getNumVertices();
for (int i = 0; i < numVertices; i++) {
pvalues.push_back((vert[i]).x);
pvalues.push_back((vert[i]).y);
pvalues.push_back((vert[i]).z);
tvalues.push_back((tex[i]).s);
tvalues.push_back((tex[i]).t);
nvalues.push_back((norm[i]).x);
nvalues.push_back((norm[i]).y);
nvalues.push_back((norm[i]).z);
}
glGenVertexArrays(1, vao);
glBindVertexArray(vao[0]);
//put the vertices into buffer #0
glGenBuffers(4, vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER, pvalues.size() * 4, &pvalues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER, tvalues.size() * 4, &tvalues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
glBufferData(GL_ARRAY_BUFFER, nvalues.size() * 4, &nvalues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ind.size() * 4, &ind[0], GL_STATIC_DRAW);
}
void init(GLFWwindow* window) {
renderingProgram = Utils::createShaderProgram("Torus_Texture_vert.glsl", "Torus_Texture_frag.glsl");
glfwGetFramebufferSize(window, &width, &height);
aspect = (float)width / (float)height;
pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.0f);//1.0472 radians=60degrees
cameraX = 1.5f; cameraY = -3.6f; cameraZ = 10.0f;
pyrLocX = -0.6f; pyrLocY = 0.7f; pyrLocZ = -5.0f;//注意方位设置的问题,很可能因为方位设置使得某物体在窗口不可见
brickTexture = Utils::loadTexture("imgs/donut.jpg");
setupVertices();
}
void display(GLFWwindow* window, double currentTime)
{
glClear(GL_DEPTH_BUFFER_BIT);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(renderingProgram);
//get the uniform variables for the MV and projection matrices
mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");//get location of uniforms in the shader program
projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");
//build view matrix,model matrix,and model-view matrix
vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));
//draw the cube(use buffer #0)
mvMat = vMat * mMat;
glEnable(GL_CULL_FACE);
glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ind.size() * 4, &ind[0], GL_STATIC_DRAW);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, brickTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDrawElements(GL_TRIANGLES, myTorus.getNumIndices(), GL_UNSIGNED_INT, 0);
}
int main(void)
{
if (!glfwInit()) {
exit(EXIT_FAILURE);
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//use OpenGL version4.3
GLFWwindow* window = glfwCreateWindow(2000, 2000, "Torus shadered by Donut Texture", NULL, NULL);//the last
//two parameters allow for full screen mode and resource sharing
glfwMakeContextCurrent(window);
if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); }
glfwSwapInterval(1);
init(window);
while (!glfwWindowShouldClose(window))
{
display(window, glfwGetTime());
glfwSwapBuffers(window);
glfwPollEvents();//handle other window related events(such as the user clicking the "X"
//in the upper right corner,the window module)
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
return 0;
}
Torus.hpp:
#pragma once
#include<cmath>
#include<vector>
#include<glm\glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
class Torus
{
private:
int numVertices;
int numIndices;
int prec;
float inner;
float outer;
std::vector<int> indices;
std::vector<glm::vec3> vertices;
std::vector<glm::vec2> texCoords;
std::vector<glm::vec3> normals;
std::vector<glm::vec3> sTangents;
std::vector<glm::vec3> tTangents;
void init();
float toRadians(float degrees);
public:
Torus();
Torus(float innerRadius, float outerRadius, int prec);
int getNumVertices();
int getNumIndices();
std::vector<int> getIndices();
std::vector<glm::vec3> getVertices();
std::vector<glm::vec2> getTexCoords();
std::vector<glm::vec3> getNormals();
std::vector<glm::vec3> getStangents();
std::vector<glm::vec3> getTtangents();
};
Torus.cpp:
#include<cmath>
#include<vector>
#include<iostream>
#include "Torus.hpp"
using namespace std;
Torus::Torus() {
prec = 48;
inner = 5.0f;
outer = 2.0f;
init();
}
Torus::Torus(float innerRadius, float outerRadius, int precIn) {
prec = precIn;
inner = innerRadius;
outer = outerRadius;
init();
}
inline float Torus::toRadians(float degrees) { return (degrees * 2.0f * 3.14159f) / 360.0f; }
void Torus::init() {
numVertices = (prec + 1) * (prec + 1);
numIndices = prec * prec * 6;
for (int i = 0; i < numVertices; i++) {
vertices.push_back(glm::vec3());
texCoords.push_back(glm::vec2());
normals.push_back(glm::vec3());
sTangents.push_back(glm::vec3());
tTangents.push_back(glm::vec3());
}
for (int i = 0; i < numIndices; i++) { indices.push_back(0); }
for (int i = 0; i < prec + 1; i++) {
float amt = toRadians(i * 360.0f / prec);
//build the ring by rotating points around the origin,then moving them outward
glm::mat4 rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 0.0f, 1.0f));
glm::vec3 initPos(rMat * glm::vec4(outer, 0.0f, 0.0f, 1.0f));
vertices[i] = glm::vec3(initPos+glm::vec3(inner,0.0f,0.0f));
//compute texture coordinates for each vertex on the ring
texCoords[i] = glm::vec2(0.0f, ((float)i / (float)prec));
//compute tangents and noramals -- first tangent is Y-axis rotated around Z
rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 0.0f, 1.0f));
tTangents[i] = glm::vec3(rMat * glm::vec4(0.0f, -1.0f, 0.0f, 1.0f));
sTangents[i] = glm::vec3(glm::vec3(0.0f, 0.0f, -1.0f));
normals[i] = glm::cross(tTangents[i], sTangents[i]);
for (int ring = 1; ring < prec + 1; ring++) {
for (int vert = 0; vert < prec + 1; vert++) {
//rotate the vertex position of the original ring around the Y axis
float amt = (float)(toRadians(ring * 360.0f / prec));
glm::mat4 rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 1.0f, 0.0f));
vertices[ring * (prec + 1) + i] = glm::vec3(rMat * glm::vec4(vertices[i], 1.0f));
//compute the texture coordinates for the vertices in the new rings
texCoords[ring * (prec + 1) + vert] = glm::vec2((float)ring * 2.0f / (float)prec, texCoords[vert].t);
if (texCoords[ring * (prec + 1) + i].s > 1.0) texCoords[ring * (prec + 1) + i].s -= 1.0f;
//rotate the tangent and bitangent vectors around the Y axis
rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 1.0f, 0.0f));
sTangents[ring * (prec + 1) + i] = glm::vec3(rMat * glm::vec4(sTangents[i], 1.0f));
rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 1.0f, 0.0f));
tTangents[ring * (prec + 1) + i] = glm::vec3(rMat * glm::vec4(tTangents[i], 1.0f));
//rotate the normal vector around the Y axis
rMat = glm::rotate(glm::mat4(1.0f), amt, glm::vec3(0.0f, 1.0f, 0.0f));
normals[ring * (prec + 1) + i] = glm::vec3(rMat * glm::vec4(normals[i], 1.0f));
}
}
//calculate triangle indices corresponding to the two triangles built per vertex
for (int ring = 0; ring < prec; ring++) {
for (int vert = 0; vert < prec; vert++) {
indices[((ring * prec + vert) * 2) * 3 + 0] = ring * (prec + 1) + vert;
indices[((ring * prec + vert) * 2) * 3 + 1] = (ring + 1) * (prec + 1) + vert;
indices[((ring * prec + vert) * 2) * 3 + 2] = ring * (prec + 1) + vert + 1;
indices[((ring * prec + vert) * 2 + 1) * 3 + 0] = ring * (prec + 1) + vert + 1;
indices[((ring * prec + vert) * 2 + 1) * 3 + 1] = (ring + 1) * (prec + 1) + vert;
indices[((ring * prec + vert) * 2 + 1) * 3 + 2] = (ring + 1) * (prec + 1) + vert + 1;
}
}
}
}
//accessors for the torus indices and vertices
int Torus::getNumVertices() { return numVertices; }
int Torus::getNumIndices() { return numIndices; }
std::vector<int> Torus::getIndices() { return indices; }
std::vector<glm::vec3> Torus::getVertices() { return vertices; }
std::vector<glm::vec2> Torus::getTexCoords() { return texCoords; }
std::vector<glm::vec3> Torus::getNormals() { return normals; }
std::vector<glm::vec3> Torus::getStangents() { return sTangents; }
std::vector<glm::vec3> Torus::getTtangents() { return tTangents; }
着色器Torus_Texture_frag.glsl
#version 430
in vec2 tc;
out vec4 color;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
layout (binding=0) uniform sampler2D samp;
void main(void)
{
color=texture(samp,tc);
}
着色器Torus_Texture_vert.glsl
#version 430
layout(location=0) in vec3 pos;
layout(location=1) in vec2 texCoord;
out vec2 tc; //texture coordinate output to rasterizer for interpolation
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
layout (binding=0) uniform sampler2D samp;
void main(void)
{
gl_Position = proj_matrix * mv_matrix * vec4(pos,1.0);
tc= texCoord;
}
纹理图片在draw_Torus.cpp所在目录下的imgs文件夹下