第一个3D程序!
运行效果
CPP
#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <soil2/SOIL2.h>
#define PI 3.14159f
#define numVAOs 1
#define numVBOs 128
#define SPEED 0.1f
#define ANGLE 0.01f
using namespace std;
GLuint rendering_program;
GLuint vao[numVAOs];
GLuint vbo[numVBOs];
GLuint vbo_count = 0;
GLuint mvloc, ploc, nloc;
GLuint clr_loc;
GLuint global_amb_loc, dirlight_amb_loc, dirlight_dif_loc, dirlight_dir_loc;
int HEIGHT = 724, WIDTH = 1024;
float aspect = (float)WIDTH / (float)HEIGHT;
glm::vec4 GlobalAmbient = { 0.6f, 0.6f, 0.6f, 1.0f }; //全局环境光
glm::vec4 DirLightAmbient = { 0.2f, 0.2f, 0.2f, 1.0f }; //定向光:环境特征
glm::vec4 DirLightDiffuse = { 0.5f, 0.5f, 0.5f, 1.0f };
glm::vec3 DirLightDirection = { -1.0f, -1.732f, 0.0f };
glm::mat4 tmat; //平移
glm::mat4 rmat; //旋转
glm::mat4 vmat; //t_mat * r_mat
glm::mat4 pmat; //透视
glm::mat4 mvmat;
glm::mat4 invmat; //mv矩阵的逆
struct Camera {
float x, y, z;
float theta, fine;
}camera;
struct Tetrahedron {
GLuint vbo_index[2] = { 0 };
glm::vec3 position = { 0.0f, 0.0f, 0.0f };
float vertex_positions[36] = {
0.0f, -0.408f, 1.155f, -1.0f, -0.408f, -0.577f, 1.0f, -0.408f, -0.577f,
0.0f, -0.408f, 1.155f, 0.0f, 1.225f, 0.0f, -1.0f, -0.408f, -0.577f,
0.0f, -0.408f, 1.155f, 1.0f, -0.408f, -0.577f, 0.0f, 1.225f, 0.0f,
-1.0f, -0.408f, -0.577f, 0.0f, 1.225f, 0.0f, 1.0f, -0.408f, -0.577f
};
float vertex_normal[36] = {
0.0f, 1.225f, 0.0f, 0.0f, 1.225f, 0.0f, 0.0f, 1.225f, 0.0f,
1.0f, -0.408f, -0.577f, 1.0f, -0.408f, -0.577f, 1.0f, -0.408f, -0.577f,
-1.0f, -0.408f, -0.577f, -1.0f, -0.408f, -0.577f, -1.0f, -0.408f, -0.577f,
0.0f, -0.408f, 1.155f, 0.0f, -0.408f, 1.155f, 0.0f, -0.408f, 1.155f
};
void init(float x, float y, float z) {
vbo_index[0] = vbo_count++;
vbo_index[1] = vbo_count++;
position = { x, y, z };
glBindBuffer(GL_ARRAY_BUFFER, vbo[vbo_index[0]]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_positions), vertex_positions, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo[vbo_index[1]]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_normal), vertex_normal, GL_STATIC_DRAW);
}
}tetrahedron;
struct Plane {
GLuint vbo_index[2] = { 0 };
glm::vec3 position = { 0.0f, 0.0f, 0.0f };
float vertex_positions[18] = {
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
};
float vertex_normal[18] = {
0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f
};
void init(float x, float y, float z) {
vbo_index[0] = vbo_count++;
vbo_index[1] = vbo_count++;
position = { x, y, z };
glBindBuffer(GL_ARRAY_BUFFER, vbo[vbo_index[0]]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_positions), vertex_positions, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vbo[vbo_index[1]]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_normal), vertex_normal, GL_STATIC_DRAW);
}
}plane;
string ReadShaderSource(const char* file_path) {
string content;
string line = "";
ifstream file_stream(file_path, ios::in);
while (!file_stream.eof()) {
getline(file_stream, line);
content.append(line + "\n");
}
file_stream.close();
return content;
}
void PrintShaderLog(GLuint shader) {
int len = 0;
int ch_writtn = 0;
char* log;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
if (len > 0) {
log = (char*)malloc(len);
glGetShaderInfoLog(shader, len, &ch_writtn, log);
cout << "Shader Info Log:" << log << endl;
free(log);
}
}
void PrintProgramLog(GLuint program) {
int len = 0;
int ch_writtn = 0;
char* log;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &len);
if (len > 0) {
log = (char*)malloc(len);
glGetProgramInfoLog(program, len, &ch_writtn, log);
cout << "Program Info Log:" << log << endl;
free(log);
}
}
bool CheckOpenGLError() {
bool found_error = false;
int glerr = glGetError();
while (glerr != GL_NO_ERROR) {
cout << "GL ERROR:" << glerr << endl;
found_error = true;
glerr = glGetError();
}
return found_error;
}
GLuint CreateShaderProgram() {
GLuint vshader = glCreateShader(GL_VERTEX_SHADER);
GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER);
GLint vert_compiled;
GLint frag_compiled;
GLint linked;
string vert_shader_string = ReadShaderSource("vert_shader.glsl");
string frag_shader_string = ReadShaderSource("frag_shader.glsl");
const char* vert_shader_source = vert_shader_string.c_str();
const char* frag_shader_source = frag_shader_string.c_str();
glShaderSource(vshader, 1, &vert_shader_source, NULL);
glShaderSource(fshader, 1, &frag_shader_source, NULL);
glCompileShader(vshader);
CheckOpenGLError();
glGetShaderiv(vshader, GL_COMPILE_STATUS, &vert_compiled);
if (vert_compiled != 1) {
cout << "vertex compilation failed" << endl;
PrintShaderLog(vshader);
}
glCompileShader(fshader);
CheckOpenGLError();
glGetShaderiv(fshader, GL_COMPILE_STATUS, &frag_compiled);
if (frag_compiled != 1) {
cout << "fragment compilation failed" << endl;
PrintShaderLog(fshader);
}
GLuint program = glCreateProgram();
glAttachShader(program, vshader);
glAttachShader(program, fshader);
glLinkProgram(program);
CheckOpenGLError();
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if (linked != 1) {
cout << "linking failed" << endl;
PrintProgramLog(program);
}
return program;
}
GLuint LoadTexture(const char* ImagePath) {
GLuint textureID;
textureID = SOIL_load_OGL_texture(ImagePath, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
if (textureID == 0) cout << "could not find texture file:" << ImagePath << endl;
return textureID;
}
void init(GLFWwindow* window) {
vmat = glm::mat4(1.0f);
rmat = glm::mat4(1.0f);
pmat = glm::perspective(1.3f, aspect, 0.1f, 1000.0f);
rendering_program = CreateShaderProgram();
glGenVertexArrays(numVAOs, vao);
glBindVertexArray(vao[0]);
glGenBuffers(numVBOs, vbo);
tetrahedron.init(4.0f, 4.0f, 4.0f);
plane.init(0.0f, 0.0f, 0.0f);
}
void MoveCamera(GLFWwindow* window) {
bool translate = false;
bool rotato = false;
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
camera.x -= (float)sin(camera.theta) * SPEED;
camera.z -= (float)cos(camera.theta) * SPEED;
translate = true;
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
camera.x += (float)sin(camera.theta) * SPEED;
camera.z += (float)cos(camera.theta) * SPEED;
translate = true;
}
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
camera.x -= (float)cos(camera.theta) * SPEED;
camera.z += (float)sin(camera.theta) * SPEED;
translate = true;
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
camera.x += (float)cos(camera.theta) * SPEED;
camera.z -= (float)sin(camera.theta) * SPEED;
translate = true;
}
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) {
camera.y += SPEED;
translate = true;
}
if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) {
camera.y -= SPEED;
translate = true;
}
if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
if (camera.fine < PI / 2.0) {
camera.fine += ANGLE;
rotato = true;
}
}
if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
if (camera.fine > -PI / 2.0) {
camera.fine -= ANGLE;
rotato = true;
}
}
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {
camera.theta += ANGLE;
rotato = true;
}
if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
camera.theta -= ANGLE;
rotato = true;
}
if (translate) tmat = glm::translate(glm::mat4(1.0f), glm::vec3(-camera.x, -camera.y, -camera.z));
if (rotato) rmat = {
{ cos(camera.theta) , sin(camera.fine) * sin(camera.theta), cos(camera.fine) * sin(camera.theta), 0.0f },
{ 0.0f , cos(camera.fine) , -sin(camera.fine) , 0.0f },
{ -sin(camera.theta) , sin(camera.fine) * cos(camera.theta), cos(camera.fine) * cos(camera.theta), 0.0f },
{ 0.0f , 0.0f , 0.0f , 1.0f }
};
if (translate || rotato) vmat = rmat * tmat;
}
void WindowReshapeCallback(GLFWwindow* window, int new_width, int new_height) {
WIDTH = new_width;
HEIGHT = new_height;
aspect = (float)WIDTH / (float)HEIGHT;
pmat = glm::perspective(1.3f, aspect, 0.1f, 1000.0f);
glViewport(0, 0, WIDTH, HEIGHT);
}
void InstallLight() {
global_amb_loc = glGetUniformLocation(rendering_program, "GlobalAmbient");
dirlight_amb_loc = glGetUniformLocation(rendering_program, "DirLightAmbient");
dirlight_dif_loc = glGetUniformLocation(rendering_program, "DirLightDiffuse");
dirlight_dir_loc = glGetUniformLocation(rendering_program, "DirLightDirection");
glProgramUniform4fv(rendering_program, global_amb_loc, 1, glm::value_ptr(GlobalAmbient));
glProgramUniform4fv(rendering_program, dirlight_amb_loc, 1, glm::value_ptr(DirLightAmbient));
glProgramUniform4fv(rendering_program, dirlight_dif_loc, 1, glm::value_ptr(DirLightDiffuse));
glProgramUniform3fv(rendering_program, dirlight_dir_loc, 1, glm::value_ptr(DirLightDirection));
}
void display(GLFWwindow* window, double curretTime) {
glClear(GL_DEPTH_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(rendering_program);
InstallLight();
mvloc = glGetUniformLocation(rendering_program, "mvmat");
ploc = glGetUniformLocation(rendering_program, "pmat");
nloc = glGetUniformLocation(rendering_program, "nmat");
clr_loc = glGetUniformLocation(rendering_program, "texture_color");
glDisable(GL_CULL_FACE);
for (int x = -32; x < 32; x++)
for (int z = -32; z < 32; z++) {
glm::vec4 texture_color(0.0f, 0.0f, 1.0f, 1.0f);
glm::mat4 mmat_p = glm::translate(glm::mat4(1.0f), glm::vec3((float)x, 0.0f, (float)z));
if ((x + z) & 1) texture_color = { 0.0f, 1.0f, 0.0f, 1.0f };
mvmat = vmat * mmat_p;
invmat = glm::transpose(glm::inverse(mvmat));
glUniformMatrix4fv(mvloc, 1, GL_FALSE, glm::value_ptr(mvmat));
glUniformMatrix4fv(ploc, 1, GL_FALSE, glm::value_ptr(pmat));
glUniformMatrix4fv(nloc, 1, GL_FALSE, glm::value_ptr(invmat));
glUniform4fv(clr_loc, 1, glm::value_ptr(texture_color));
glBindBuffer(GL_ARRAY_BUFFER, vbo[plane.vbo_index[0]]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[plane.vbo_index[1]]);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
glEnable(GL_CULL_FACE);
glm::mat4 mmat_t = glm::translate(glm::mat4(1.0f), tetrahedron.position);
mvmat = vmat * mmat_t;
invmat = glm::transpose(glm::inverse(mvmat));
glUniformMatrix4fv(mvloc, 1, GL_FALSE, glm::value_ptr(mvmat));
glUniformMatrix4fv(ploc, 1, GL_FALSE, glm::value_ptr(pmat));
glUniformMatrix4fv(nloc, 1, GL_FALSE, glm::value_ptr(invmat));
glUniform4fv(clr_loc, 1, glm::value_ptr(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)));
glBindBuffer(GL_ARRAY_BUFFER, vbo[tetrahedron.vbo_index[0]]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo[tetrahedron.vbo_index[1]]);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDrawArrays(GL_TRIANGLES, 0, 12);
}
int main() {
if (!glfwInit()) { exit(EXIT_FAILURE); }
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "HelloWorld", NULL, NULL);
glfwMakeContextCurrent(window);
if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); }
glfwSwapInterval(1);
glfwSetWindowSizeCallback(window, WindowReshapeCallback);
init(window);
while (!glfwWindowShouldClose(window)) {
MoveCamera(window);
display(window, glfwGetTime());
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
顶点着色器
#version 430
layout (location=0) in vec3 position;
layout (location=1) in vec3 normal;
out vec3 varying_normal;
out vec3 varying_direction;
uniform mat4 mvmat;
uniform mat4 pmat;
uniform mat4 nmat;
uniform vec4 texture_color;
h
uniform vec4 GlobalAmbient;
uniform vec4 DirLightAmbient;
uniform vec4 DirLightDiffuse;
uniform vec3 DirLightDirection;
void main(void) {
varying_normal = (nmat * vec4(normal, 1.0)).xyz;
varying_direction = (nmat * vec4(DirLightDirection, 1.0)).xyz;
gl_Position = pmat * mvmat * vec4(position, 1.0);
}
片段着色器
#version 430
in vec3 varying_normal;
in vec3 varying_direction;
out vec4 color;
uniform mat4 mvmat;
uniform mat4 pmat;
uniform mat4 nmat;
uniform vec4 texture_color;
uniform vec4 GlobalAmbient;
uniform vec4 DirLightAmbient;
uniform vec4 DirLightDiffuse;
uniform vec3 DirLightDirection;
void main(void) {
vec3 L = normalize(varying_direction);
vec3 N = normalize(varying_normal);
float cos_theta = dot(L, N);
vec3 ambient = GlobalAmbient.xyz * texture_color.xyz + DirLightAmbient.xyz * texture_color.xyz;
vec3 diffuse = texture_color.xyz * DirLightDiffuse.xyz * max(cos_theta, 0.0);
color = vec4(ambient + diffuse, 1.0);
}