cpp
复制代码
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <random>
#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>
const float PI = acos(-1.0f);
#include "utils.h"
#include "geometry.h"
#include "noise.h"
#include "penguin.h"
const float SPEED = 0.1f;
const float ANGLE = 0.03f;
GLuint rendering_program;
GLuint shadow_program;
GLuint plain_program;
GLuint snow_program;
GLuint sky_program;
GLuint sky_texture;
GLuint snow_texture;
GLuint shadow_depth;
GLuint shadow_buffer;
GLuint shadow_texture;
GLuint temp_location; //临时
GLuint time_location; //时间
GLuint global_amb_loc;
int HEIGHT = 724, WIDTH = 1024;
float aspect = (float)WIDTH / (float)HEIGHT;
float direction_light_angle = PI / 2.0f;
glm::mat4 proj_mat;
glm::vec4 global_ambient = { 0.2f, 0.2f, 0.2f, 1.0f }; //全局环境光
Camera camera;
Vertex_Manager vm;
DirectionLight sunshine;
Object* square;
Object* snow;
Object* tetrahedron;
Object* icosahedron;
Object* cube;
Object* cone;
Object* sky;
Object* sphere;
Object* water_droplet;
Object* surface;
Penguin* penguin;
enum Weather {
sunny,
cloudy,
snowy
}weather;
static void SetupShadowBuffers(GLFWwindow* window) {
GLuint buffer_id[1]{};
glGenBuffers(1, buffer_id);
glfwGetFramebufferSize(window, &WIDTH, &HEIGHT);
glGenFramebuffers(1, buffer_id);
shadow_buffer = buffer_id[0];
glBindFramebuffer(GL_FRAMEBUFFER, shadow_buffer);
glGenTextures(1, buffer_id);
shadow_texture = buffer_id[0];
glBindTexture(GL_TEXTURE_2D, shadow_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, WIDTH, HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shadow_texture, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glGenTextures(1, buffer_id);
shadow_depth = buffer_id[0];
glBindTexture(GL_TEXTURE_2D, shadow_depth);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, WIDTH, HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadow_depth, 0);
}
static void init(GLFWwindow* window) {
camera.vmat = glm::mat4(1.0f);
camera.tmat = glm::mat4(1.0f);
camera.rmat = glm::mat4(1.0f);
camera.pmat = glm::perspective(1.4f, aspect, 0.2f, 500.0f);
proj_mat = ortho_matrix(sunshine.direction, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
rendering_program = CreateShaderProgram("vert_shader.glsl", "frag_shader.glsl");
shadow_program = CreateShaderProgram("shadow_vert.glsl", "shadow_frag.glsl");
plain_program = CreateShaderProgram("plain_vert.glsl", "plain_frag.glsl");
snow_program = CreateShaderProgram("snow_vert.glsl", "snow_frag.glsl");
sky_program = CreateShaderProgram("sky_vert.glsl", "sky_frag.glsl");
sky_texture = CreateNoisy3DTexture256(glm::vec4(0.0f, 0.5f, 1.0f, 1.0f), glm::vec4(1.0f, 1.0f, 1.0f, 1.0f), MD);
snow_texture = CreateRandom2DTexture512();
SetupShadowBuffers(window);
glGenVertexArrays(numVAOs, vm.vao);
glBindVertexArray(vm.vao[0]);
glGenBuffers(numVBOs, vm.vbo);
glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
square = CreateSquare(&vm, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
snow = CreateSnow(&vm, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec4(0.8f, 0.8f, 0.8f, 1.0f));
tetrahedron = CreateTetrahedron(&vm, glm::vec3(6.0f, 2.0f, 0.0f), glm::vec4(1.0f, 0.0f, 0.0f, 0.0f));
icosahedron = CreateIcosahedron(&vm, glm::vec3(6.0f, 4.0f, 0.0f), glm::vec4(1.0f, 0.0f, 0.0f, 0.0f));
cube = CreateCube(&vm, glm::vec3(6.0f, 6.0f, 0.0f), glm::vec4(1.0f, 0.0f, 0.0f, 0.0f));
cone = CreateCone(&vm, glm::vec3(6.0f, 8.0f, 0.0f), glm::vec4(1.0f, 0.0f, 0.0f, 10.0f), PI / 6.0f);
float* chunk = new float[80000];
sky = CreateSphere(&vm, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), chunk, 80000);
sphere = CreateSphere(&vm, glm::vec3(6.0f, 10.0f, 0.0f), glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), chunk, 80000);
water_droplet = CreateWaterDroplet(&vm, glm::vec3(6.0f, 12.0f, 0.0f), glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), chunk, 80000);
glm::vec3 p[4][4] = {
glm::vec3(-1.0f,0.0f, -1.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 1.0f), glm::vec3(-1.0f, 0.0f, 2.0f),
glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 1.0f), glm::vec3(0.0f, 0.0f, 2.0f),
glm::vec3(1.0f, 0.0f, -1.0f), glm::vec3(1.0f, 1.0f, 0.0f), glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(1.0f, 0.0f, 2.0f),
glm::vec3(2.0f, 0.0f, -1.0f), glm::vec3(2.0f, 0.0f, 0.0f), glm::vec3(2.0f, 0.0f, 1.0f), glm::vec3(2.0f, 0.0f, 2.0f)
};
surface = CreateSurface(&vm, glm::vec3(0.0f, 4.0f, 0.0f), glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), p, chunk, 80000);
penguin = CreatePenguin(&vm, chunk, 80000);
delete[] chunk;
}
static void MoveCamera(GLFWwindow* window) {
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
camera.position[0] -= (float)sin(camera.theta) * SPEED;
camera.position[2] -= (float)cos(camera.theta) * SPEED;
}
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
camera.position[0] += (float)sin(camera.theta) * SPEED;
camera.position[2] += (float)cos(camera.theta) * SPEED;
}
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
camera.position[0] -= (float)cos(camera.theta) * SPEED;
camera.position[2] += (float)sin(camera.theta) * SPEED;
}
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
camera.position[0] += (float)cos(camera.theta) * SPEED;
camera.position[2] -= (float)sin(camera.theta) * SPEED;
}
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) {
camera.position[1] += SPEED;
}
if (glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS) {
camera.position[1] -= SPEED;
}
if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
if (camera.phi < PI / 2.0) {
camera.phi += ANGLE;
}
}
if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
if (camera.phi > -PI / 2.0) {
camera.phi -= ANGLE;
}
}
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {
camera.theta += ANGLE;
}
if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
camera.theta -= ANGLE;
}
if (glfwGetKey(window, GLFW_KEY_T) == GLFW_PRESS) {
direction_light_angle += ANGLE;
if (direction_light_angle >= PI) direction_light_angle -= PI * 2;
sunshine.direction = { -cos(direction_light_angle), -0.866f * sin(direction_light_angle), -0.5f * sin(direction_light_angle)};
proj_mat = ortho_matrix(sunshine.direction, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glm::vec4 m = glm::vec4(0.35f, 0.4f, 0.45f, 1.0f);
glm::vec4 r = glm::vec4(0.35f, 0.3f, 0.25f, 0.0f);
glm::vec4 c = m + sin(direction_light_angle) * r;
glClearColor(c[0], c[1], c[2], c[3]);
}
if (glfwGetKey(window, GLFW_KEY_Y) == GLFW_PRESS) {
weather = sunny;
}
if (glfwGetKey(window, GLFW_KEY_U) == GLFW_PRESS) {
weather = cloudy;
}
if (glfwGetKey(window, GLFW_KEY_I) == GLFW_PRESS) {
weather = snowy;
}
camera.tmat = glm::translate(glm::mat4(1.0f), -camera.position);
camera.rmat = {
{ cos(camera.theta) , sin(camera.phi) * sin(camera.theta) , cos(camera.phi) * sin(camera.theta) , 0.0f },
{ 0.0f , cos(camera.phi) , -sin(camera.phi) , 0.0f },
{ -sin(camera.theta) , sin(camera.phi) * cos(camera.theta) , cos(camera.phi) * cos(camera.theta) , 0.0f },
{ 0.0f , 0.0f , 0.0f , 1.0f }
};
camera.vmat = camera.rmat * camera.tmat;
}
static void WindowReshapeCallback(GLFWwindow* window, int new_width, int new_height) {
WIDTH = new_width;
HEIGHT = new_height;
aspect = (float)WIDTH / (float)HEIGHT;
camera.pmat = glm::perspective(1.4f, aspect, 0.2f, 500.0f);
glViewport(0, 0, WIDTH, HEIGHT);
glDeleteTextures(1, &shadow_depth);
glDeleteTextures(1, &shadow_texture);
glDeleteFramebuffers(1, &shadow_buffer);
SetupShadowBuffers(window);
}
static void InstallLight(GLuint program) {
global_amb_loc = glGetUniformLocation(program, "GlobalAmbient");
sunshine.dirlight_amb_loc = glGetUniformLocation(program, "DirLightAmbient");
sunshine.dirlight_dif_loc = glGetUniformLocation(program, "DirLightDiffuse");
sunshine.dirlight_spe_loc = glGetUniformLocation(program, "DirLightSpecular");
sunshine.dirlight_dir_loc = glGetUniformLocation(program, "DirLightDirection");
glProgramUniform4fv(program, global_amb_loc, 1, glm::value_ptr(global_ambient));
glProgramUniform4fv(program, sunshine.dirlight_amb_loc, 1, glm::value_ptr(sunshine.direction_light_ambient));
glProgramUniform4fv(program, sunshine.dirlight_dif_loc, 1, glm::value_ptr(sunshine.direction_light_diffuse));
glProgramUniform4fv(program, sunshine.dirlight_spe_loc, 1, glm::value_ptr(sunshine.direction_light_specular));
glProgramUniform3fv(program, sunshine.dirlight_dir_loc, 1, glm::value_ptr(sunshine.direction));
}
static void InstallShadow(GLuint shadow_program) {
camera.vloc = glGetUniformLocation(shadow_program, "vmat");
camera.ploc = glGetUniformLocation(shadow_program, "pmat");
camera.mvloc = glGetUniformLocation(shadow_program, "proj"); //临时替用
}
static void InstallCamera(GLuint program) {
camera.mvloc = glGetUniformLocation(program, "mvmat");
camera.ploc = glGetUniformLocation(program, "pmat");
camera.vloc = glGetUniformLocation(program, "vmat");
camera.cloc = glGetUniformLocation(program, "texture_color");
}
static void display(GLFWwindow* window, double current_time) {
/*第一轮:绘制阴影,记录颜色*/
glBindFramebuffer(GL_FRAMEBUFFER, shadow_buffer);
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDepthFunc(GL_LEQUAL);
glUseProgram(shadow_program);
InstallShadow(shadow_program);
DrawShadow(&camera, &vm, cone, proj_mat * glm::translate(glm::mat4(1.0f), cone->position));
DrawShadow(&camera, &vm, surface, proj_mat * glm::translate(glm::mat4(1.0f), surface->position));
DrawShadow(&camera, &vm, cube, proj_mat * glm::translate(glm::mat4(1.0f), cube->position));
DrawShadow(&camera, &vm, tetrahedron, proj_mat * glm::translate(glm::mat4(1.0f), tetrahedron->position));
DrawShadow(&camera, &vm, icosahedron, proj_mat * glm::translate(glm::mat4(1.0f), icosahedron->position));
DrawShadow(&camera, &vm, sphere, proj_mat * glm::translate(glm::mat4(1.0f), sphere->position));
DrawShadow(&camera, &vm, water_droplet, proj_mat * glm::translate(glm::mat4(1.0f), water_droplet->position));
DrawShadow(&camera, &vm, penguin->penguin_model, proj_mat * glm::translate(glm::mat4(1.0f), penguin->penguin_model->position) *
glm::rotate(glm::mat4(1.0f), PI, glm::vec3(0.0f, 1.0f, 0.0f)));
/*第二轮:绘制除阴影外的物体*/
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
if (weather == sunny) {
glUseProgram(sky_program);
temp_location = glGetUniformLocation(sky_program, "light_angle");
camera.vloc = glGetUniformLocation(sky_program, "light_direction"); //临时替用
camera.mvloc = glGetUniformLocation(sky_program, "mvmat");
camera.ploc = glGetUniformLocation(sky_program, "pmat");
camera.mvmat = camera.vmat * glm::translate(glm::mat4(1.0f), camera.position);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_3D, sky_texture);
glUniform1f(temp_location, direction_light_angle);
glUniform3fv(camera.vloc, 1, glm::value_ptr(sunshine.direction));
glUniformMatrix4fv(camera.mvloc, 1, GL_FALSE, glm::value_ptr(camera.mvmat));
glUniformMatrix4fv(camera.ploc, 1, GL_FALSE, glm::value_ptr(camera.pmat));
glBindBuffer(GL_ARRAY_BUFFER, vm.vbo[sky->vbo_index[0]]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glDrawArrays(GL_TRIANGLES, 0, sky->size / 3);
}
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
if (weather == snowy) {
glUseProgram(snow_program);
temp_location = glGetUniformLocation(snow_program, "camera");
time_location = glGetUniformLocation(snow_program, "current_time");
camera.mvloc = glGetUniformLocation(snow_program, "mvmat");
camera.ploc = glGetUniformLocation(snow_program, "pmat");
camera.cloc = glGetUniformLocation(snow_program, "texture_color");
camera.mvmat = camera.vmat;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, snow_texture);
glUniformMatrix4fv(camera.mvloc, 1, GL_FALSE, glm::value_ptr(camera.mvmat));
glUniformMatrix4fv(camera.ploc, 1, GL_FALSE, glm::value_ptr(camera.pmat));
glUniform4fv(camera.cloc, 1, glm::value_ptr(snow->texture_color));
glUniform3fv(temp_location, 1, glm::value_ptr(camera.position));
glUniform1f(time_location, (GLfloat)current_time);
glBindBuffer(GL_ARRAY_BUFFER, vm.vbo[snow->vbo_index[0]]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glDrawArraysInstanced(GL_TRIANGLES, 0, snow->size / 3, 262144);
}
glUseProgram(plain_program);
InstallLight(plain_program);
InstallCamera(plain_program);
for (int x = -32; x < 32; x++) {
for (int z = -32; z < 32; z++) {
square->position = glm::vec3(float(x) + 0.5f, 0.0f, float(z) + 0.5f);
square->texture_color = glm::vec4(0.0f, float((x + z) & 1), float(!bool((x + z) & 1)), 0.0f);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, shadow_texture);
DrawObject(&camera, &vm, square);
}
}
glUseProgram(rendering_program);
InstallLight(rendering_program);
InstallCamera(rendering_program);
DrawObject(&camera, &vm, cone);
DrawObject(&camera, &vm, surface);
glEnable(GL_CULL_FACE);
DrawObject(&camera, &vm, cube);
DrawObject(&camera, &vm, tetrahedron);
DrawObject(&camera, &vm, icosahedron);
DrawObject(&camera, &vm, sphere);
DrawObject(&camera, &vm, water_droplet);
DrawModel(&camera, &vm, penguin->penguin_model, glm::translate(glm::mat4(1.0f), penguin->penguin_model->position) * glm::rotate(glm::mat4(1.0f), PI, glm::vec3(0.0f, 1.0f, 0.0f)));
}
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);
double pre = glfwGetTime();
while (!glfwWindowShouldClose(window)) {
MoveCamera(window);
display(window, pre);
glfwSwapBuffers(window);
glfwPollEvents();
while (glfwGetTime() - pre <= 0.02);
//cout << 1.0 / (glfwGetTime() - pre) << endl; //帧率
pre = glfwGetTime();
}
DestroyObject(square);
DestroyObject(snow);
DestroyObject(tetrahedron);
DestroyObject(icosahedron);
DestroyObject(cube);
DestroyObject(cone);
DestroyObject(sky);
DestroyObject(sphere);
DestroyObject(water_droplet);
DestroyObject(surface);
DestroyModel(penguin->penguin_model);
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}