计算机图形学作业2
本文为计算机图形学作业2报告, 为本人计算机图形学课程作业, 仅供参考, 未经允许不得转载, 抄袭.
内容应包含但不局限于项目名称,项目简介。篇幅占整体内容的10%
本次作业在第一次作业的基础上,增加一个bezier曲面,并对场景添加光照和纹理效果。具体要求如下:
编程环境
MacOS,C++11,IDE: CLion,cmake,使用OpenGL,Glad,GLFW3.3.8,glm0.9.9.1
文件结构
(base) alanzeng@AlandeMacBook-Pro CGA2 % tree
CGA2
├── 1.model_loading.fs
├── 1.model_loading.vs
├── BezierFace.cpp
├── CMakeLists.txt
├── OBJ_Files
├── TJU.jpg
├── cmake-build-debug
├── glad
├── glm-0.9.9.1
├── includes
│ ├── BezierFace.h
│ ├── camera.h
│ ├── shader.h
│ └── stb_image.h
└── model_loading.cpp
在这一部分需要描述你的项目实现过程中用了哪些算法,需要你从算法背后的原理入手结合自己的项目进行描述,切忌段落式的粘贴代码,如果是自己写的算法,进行适当的算法复杂度分析。 示例:a) Lambert 漫反射光照模型 b) 正投影相机 c) Phong光照模型 d) 一些图形的绘制举例 e) 碰撞测试特例算法
注:一定要结合自己的项目去写这一部分,落到实处,切忌直接从网上直接粘贴理论知识。
Phong光照模型是计算三维计算机图形中光照效果的经典方法之一,它被广泛用于渲染三维场景和物体。Phong光照模型考虑了三种主要的光照成分:环境光、漫反射光和镜面光。
Phong光照模型可以表示为以下方程:
其中:
在片段着色器中, 运用Phong光照模型进行计算
片段着色器中定义了Material(材质)和Light(光照)结构体
struct Material {
sampler2D diffuse;
vec3 specular;
float shininess;
};
struct Light {
vec3 position;
vec3 direction;
float cutOff;
float outerCutOff;
vec3 ambient;
vec3 diffuse;
vec3 specular;
float constant;
float linear;
float quadratic;
};
具体算法实现
void main()
{
// ambient
vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(light.position - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;
// specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = light.specular * (spec * material.specular);
// spotlight (soft edges)
float theta = dot(lightDir, normalize(-light.direction));
float epsilon = (light.cutOff - light.outerCutOff);
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
diffuse *= intensity;
specular *= intensity;
// attenuation
float distance = length(light.position - FragPos);
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
ambient *= attenuation;
diffuse *= attenuation;
specular *= attenuation;
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
}
贝塞尔曲面(Bezier Surface)是一种用于三维图形和计算机辅助设计(CAD)中的曲面表示方法。它是由法国工程师皮埃尔·贝塞尔(Pierre Bézier)于20世纪50年代发明的,并在汽车设计领域广泛应用。贝塞尔曲面由多个控制点定义,这些点控制了曲面的形状。它是一种常用的二次和三次曲面建模技术。ß
贝塞尔曲面的定义基于控制点和基函数。控制点是用来确定曲面的形状的点,而基函数是一种数学函数,它们根据控制点的位置来计算曲面上的点。一般来说,贝塞尔曲面是由两个参数u和v来定义的,这些参数的范围通常是从0到1。
一个常见的二次贝塞尔曲面可以用以下公式来表示:
其中,P0到P8是控制点的坐标,(u, v)是曲面上的点坐标。这个公式描述了如何根据控制点的位置以及参数u和v的值来计算曲面上的点。
贝塞尔曲面的优点之一是它在控制点的位置和权重上具有直观性,可以通过移动控制点来调整曲面的形状。此外,贝塞尔曲面是局部控制的,这意味着一个控制点的改变只会影响曲面上的局部区域。
在计算机图形中,贝塞尔曲面通常用于建模、渲染和处理复杂的曲面形状,例如汽车外壳、船体、飞机机身等。
本次实验中,我们要构建一个由5\times5控制点构成的贝塞尔曲面, 具体的实现请见2.3.3 BezierFace.h
纹理贴图是计算机图形学中强大且广泛应用的技术,它可以增强三维场景的真实感和视觉质量。不同类型的纹理和纹理技术可以用来模拟各种不同的视觉效果,从基本的颜色贴图到复杂的环境映射和法线贴图。
在本次实验中我们使用了stb_image库来加载贴图, 通过一下方法来具体实现纹理贴图
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
int width, height, nrComponents;
stbi_set_flip_vertically_on_load(true);
unsigned char* data = stbi_load(path, &width, &height, &nrComponents, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Texture failed to load at path: " << path << std::endl;
}
stbi_image_free(data);
return textureID;
在这一部分,将自己的项目拆解成各个部分进行说明。切忌段落式的粘贴代码。
第二章应占整体篇幅的40%。
与上课时给出的shader.h代码相同
使用C++文件流读取着色器内容,储存到几个string
对象里
以下实现了对着色器的编译和链接
unsigned int vertex, fragment;
// vertex shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// shader Program
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
// delete the shaders as they're linked into our program now and no longer necessary
glDeleteShader(vertex);
glDeleteShader(fragment);
定义了
void use() const { glUseProgram(ID); }
提供ProcessKeyboard
, ProcessMouseMovement
, ProcessMouseScroll
方法,用于实现鼠标键盘控制
Camera 类 定义了位置,接收变化的变量,欧拉角,选项和构造函数,此外还有键盘鼠标操作函数和相机更新函数
class Camera
{
public:
// camera Attributes
glm::vec3 Position;
glm::vec3 Front;
glm::vec3 Up;
glm::vec3 Right;
glm::vec3 WorldUp;
// euler Angles
float Yaw;
float Pitch;
// camera options
float MovementSpeed;
float MouseSensitivity;
float Zoom;
// constructor with vectors
Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f),
float yaw = YAW, float pitch = PITCH) :
Front(glm::vec3(0.0f, 0.0f, -1.0f)),
MovementSpeed(SPEED),
MouseSensitivity(SENSITIVITY),
Zoom(ZOOM)
{
Position = position;
WorldUp = up;
Yaw = yaw;
Pitch = pitch;
updateCameraVectors();
}
// constructor with scalar values
Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) :
Front(glm::vec3(0.0f, 0.0f, -1.0f)),
MovementSpeed(SPEED),
MouseSensitivity(SENSITIVITY),
Zoom(ZOOM)
{
Position = glm::vec3(posX, posY, posZ);
WorldUp = glm::vec3(upX, upY, upZ);
Yaw = yaw;
Pitch = pitch;
updateCameraVectors();
}
// returns the view matrix calculated using Euler Angles and the LookAt Matrix
glm::mat4 GetViewMatrix()
{
return glm::lookAt(Position, Position + Front, Up);
}
};
ProcessKeyboard处理从任何类似键盘的输入系统接收的输入
void ProcessKeyboard(Camera_Movement direction, float deltaTime)
{
float velocity = MovementSpeed * deltaTime;
if (direction == FORWARD)
Position += Front * velocity;
if (direction == BACKWARD)
Position -= Front * velocity;
if (direction == LEFT)
Position -= Right * velocity;
if (direction == RIGHT)
Position += Right * velocity;
if (direction == DOWN)
Position -= Up * velocity;
if (direction == UP)
Position += Up * velocity;
}
ProcessMouseMovement处理从鼠标输入系统接收的输入。期望x和y方向上的偏移值。
void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
{
xoffset *= MouseSensitivity;
yoffset *= MouseSensitivity;
Yaw += xoffset;
Pitch += yoffset;
// make sure that when pitch is out of bounds, screen doesn't get flipped
if (constrainPitch)
{
if (Pitch > 89.0f)
Pitch = 89.0f;
if (Pitch < -89.0f)
Pitch = -89.0f;
}
// update Front, Right and Up Vectors using the updated Euler angles
updateCameraVectors();
}
ProcessMouseScroll
处理从鼠标滚轮事件接收的输入。只需要在垂直轮轴上输入
void ProcessMouseScroll(float yoffset)
{
Zoom -= (float)yoffset;
if (Zoom < 1.0f)
Zoom = 1.0f;
if (Zoom > 45.0f)
Zoom = 45.0f;
}
updateCameraVectors
void updateCameraVectors()
{
// calculate the new Front vector
glm::vec3 front;
front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
front.y = sin(glm::radians(Pitch));
front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));
Front = glm::normalize(front);
// also re-calculate the Right and Up vector
Right = glm::normalize(glm::cross(Front, WorldUp));
Up = glm::normalize(glm::cross(Right, Front));
}
定义类贝塞尔曲面类, 其中包含顶点个数, 索引个数, 顶点, 法线, 纹理, 索引, 控制点, 曲线阶数等等属性
#pragma once
#include <vector>
#include <glm/glm.hpp>
using namespace std;
class BezierFace
{
//顶点个数
int numVertices;
//索引个数
int numIndices;
//顶点
vector<glm::vec3> vertices;
//法线
vector<glm::vec3> normals;
//纹理
vector<glm::vec2> texCoords;
//索引
vector<int> indices;
//计算数据
void init(int prec);
//控制点
float* controlPoints;
vector<glm::vec3> controlPointsVector;
//曲线阶数
int step;
float toRadians(float degrees);
float Bernstein(float u, int index);
public:
BezierFace() = default;
BezierFace(int step, float controlPoints[], int prec);
int getNumVertices();
int getNumIndices();
vector<glm::vec3> getVertices();
vector<glm::vec3> getNormals();
vector<glm::vec2> getTexCoords();
vector<int> getIndices();
};
BezierFace::BezierFace(int step, float controlPoints[], int prec)
{
this->step = step;
this->controlPoints = controlPoints;
init(prec);
}
该函数实现了Bezier曲面初始化
prec
(precision):这个参数控制曲面的精度,它影响了生成的顶点密度。我们使用 (prec + 1) * (prec + 1)
个顶点和 prec * prec * 6
个索引。vertices
、normals
和 texCoords
,以及存储索引的容器 indices
。这些容器将在后续的计算中使用。controlPoints
数组中。我们将这些控制点提取出来并存储在 controlPointsVector
容器中,以便后续计算使用。(i, j)
的参数空间,其中 i
和 j
分别表示沿 u 和 v 方向的参数值。对于每个 (i, j)
坐标点,我们使用贝塞尔曲面的公式计算了 x、y 和 z 坐标,并将它们存储在 vertices
中。texCoords
中。indices
中,以便在渲染时使用。
这个初始化过程将为Bezier曲面提供一个网格,其中包含离散的顶点、法线和纹理坐标,以及定义曲面几何的索引。在实验的后续部分,我们将使用这些数据来渲染Bezier曲面并展示其效果。
void BezierFace::init(int prec)
{
numVertices = (prec + 1) * (prec + 1);
numIndices = prec * prec * 6;
for (int i = 0; i < numVertices; i++)
{
vertices.push_back(glm::vec3());
normals.push_back(glm::vec3());
texCoords.push_back(glm::vec2());
}
for (int i = 0; i < numIndices; i++)
{
indices.push_back(0);
}
for (int i = 0; i < (step + 1) * (step + 1) * 3; i += 3)
{
controlPointsVector.push_back(glm::vec3(controlPoints[i], controlPoints[i + 1], controlPoints[i + 2]));
}
for (int i = 0; i <= prec; i++)
{
for (int j = 0; j <= prec; j++)
{
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float u = (float)i / prec;
float v = (float)j / prec;
for (int ii = 0; ii <= step; ii++)
{
for (int jj = 0; jj <= step; jj++)
{
int index = ii * (step + 1) + jj;
x += controlPointsVector[index].x * Bernstein(u, ii) * Bernstein(v, jj);
y += controlPointsVector[index].y * Bernstein(u, ii) * Bernstein(v, jj);
z += controlPointsVector[index].z * Bernstein(u, ii) * Bernstein(v, jj);
}
}
vertices[i * (prec + 1) + j] = glm::vec3(x, y, z);
normals[i * (prec + 1) + j] = glm::vec3(x, y, z);
texCoords[i * (prec + 1) + j] = glm::vec2((float)j / prec, (float)i / prec);
}
}
//计算索引
for (int i = 0; i < prec; i++) {
for (int j = 0; j < prec; j++) {
indices[6 * (i * prec + j) + 0] = i * (prec + 1) + j;
indices[6 * (i * prec + j) + 1] = i * (prec + 1) + j + 1;
indices[6 * (i * prec + j) + 2] = (i + 1) * (prec + 1) + j;
indices[6 * (i * prec + j) + 3] = i * (prec + 1) + j + 1;
indices[6 * (i * prec + j) + 4] = (i + 1) * (prec + 1) + j + 1;
indices[6 * (i * prec + j) + 5] = (i + 1) * (prec + 1) + j;
}
}
}
初始化函数中可以看到其中使用了Bernstein函数, 在Bezier曲线和Bezier曲面的计算中,Bernstein基函数(Bernstein basis functions)起着重要的作用。它们是一组多项式函数,用于确定Bezier曲线和曲面上的点的位置。
float BezierFace::Bernstein(float t, int index) {
switch (index)
{
default:
case 0:
return pow(1.0 - t, 4);
break;
case 1:
return 4 * t * pow(1.0 - t, 3);
break;
case 2:
return 6 * pow(t, 2) * pow(1.0 - t, 2);
break;
case 3:
return 4 * pow(t, 3) * (1.0 - t);
break;
case 4:
return pow(t, 4);
break;
}
}
具体实现和实验一中完全一致, 不再赘述
具体实现和实验一中完全一致, 不再赘述
主方法
Camera camera(glm::vec3(0.0f, 0.0f, 10.0f));
Shader ourShader("../1.model_loading.vs", "../1.model_loading.fs");
Model ourModel("../OBJ_Files/airboat.obj");
#define STB_IMAGE_IMPLEMENTATION
将stb_image.h
编译为cpp文件使用#define STB_IMAGE_IMPLEMENTATION
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <stb_image.h>
#include <shader.h>
#include <camera.h>
#include <model.h>
#include <mesh.h>
#include <BezierFace.h>
#include <iostream>
定义函数, 和一些基本参数
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);
void setUpVertices();
unsigned int loadTexture(char const* path);
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
// camera
Camera camera(glm::vec3(0.5f, 0.5f, 3.5f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;
// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;
// lighting
glm::vec3 lightPos(0.5f, 0.5f, 2.0f);
// Control Points
float controlPoints[] = {
-1.5, -1.5, 2.0,
-0.5, -1.5, 2.0,
0.0, -1.5, 1.0,
0.5, -1.5, -1.0,
1.5, -1.5, 2.0,
-1.5, -0.5, 1.0,
-0.5, 1.5, 2.0,
0.0, 0.5, -1.0,
0.5, 0.5, 1.0,
1.5, -0.5, -1.0,
-1.5, 0.0, 0.0,
-0.5, -1.5, -1.0,
0.0, 0.0, 0.0,
0.5, -1.0, 2.0,
1.5, 1.0, 0.0,
-1.5, 0.5, 2.0,
-0.5, 0.5, 1.0,
0.0, 0.5, 1.0,
0.5, 0.5, 1.0,
1.5, -1.5, 1.5,
-1.5, 1.5, -2.0,
-0.5, 1.5, -2.0,
0.0, 1.5, 2.0,
0.5, 0.5, 1.0,
1.5, 1.5, -1.0
};
BezierFace myBezier = BezierFace(4, controlPoints, 100);
GLuint VAO, VBO[3];
初始化, 加载窗口
这部分代码固定不变, 同时打开了深度检测
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "ModelLoading", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetMouseButtonCallback(window,mouse_button_callback);
glfwSetScrollCallback(window, scroll_callback);
// tell GLFW to capture our mouse
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// configure global opengl state
// -----------------------------
glEnable(GL_DEPTH_TEST);
配置shader和model, 以及贝塞尔曲面的顶点和纹理信息
这里贴图采用了天津大学的校旗图案
// build and compile shaders
// -------------------------
Shader ourShader("../1.model_loading.vs", "../1.model_loading.fs");
// load models
Model ourModel("../OBJ_Files/airboat.obj");
// setup Vertices
setUpVertices();
// load Textures
unsigned int texture = loadTexture("../TJU.jpg");
在循环中绘制图
其中为shader配置了参数, 因为单独添加Bezier曲面, Bezier曲面的信息单独在main函数中加载
while (!glfwWindowShouldClose(window))
{
// per-frame time logic
// --------------------
float currentFrame = static_cast<float>(glfwGetTime());
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// don't forget to enable shader before setting uniforms
ourShader.use();
// be sure to activate shader when setting uniforms/drawing objects
ourShader.use();
ourShader.setVec3("light.position", camera.Position);
ourShader.setVec3("light.direction", camera.Front);
ourShader.setFloat("light.cutOff", glm::cos(glm::radians(12.5f)));
ourShader.setVec3("viewPos", camera.Position);
// light properties
ourShader.setVec3("light.ambient", 0.1f, 0.1f, 0.1f);
// we configure the diffuse intensity slightly higher; the right lighting conditions differ with each lighting method and environment.
// each environment and lighting type requires some tweaking to get the best out of your environment.
ourShader.setVec3("light.diffuse", 0.8f, 0.8f, 0.8f);
ourShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);
ourShader.setFloat("light.constant", 1.0f);
ourShader.setFloat("light.linear", 0.09f);
ourShader.setFloat("light.quadratic", 0.032f);
// material properties
ourShader.setFloat("material.shininess", 32.0f);
ourShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f);
// view/projection transformations
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
glm::mat4 view = camera.GetViewMatrix();
ourShader.setMat4("projection", projection);
ourShader.setMat4("view", view);
// render the loaded model
glm::mat4 model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); // translate it down so it's at the center of the scene
model = glm::scale(model, glm::vec3(1.0f, 1.0f, 1.0f)); // it's a bit too big for our scene, so scale it down
ourShader.setMat4("model", model);
model = glm::mat4(1.0f);
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.2f)); // a smaller cube
ourShader.setMat4("model", model);
ourShader.setMat4("projection", projection);
ourShader.setMat4("view", view);
ourShader.setMat4("transform", glm::mat4(1.0f));
//--
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(2);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, myBezier.getNumIndices());
glBindVertexArray(0);
ourModel.Draw(ourShader);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
最后终止
glfwTerminate();
return 0;
对这些函数进行实现
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
==void setUpVertices();==
==unsigned int loadTexture(char const* path);==
前五个函数处理键盘鼠标操作, 在实验一中有具体实现, 在此不再赘述, 此处展示setUpVertices()
和loadTexture()
方法, 用于实现Bezier曲面
void setUpVertices()
{
vector<float> pvalues;
vector<float> tvalues;
vector<float> nvalues;
vector<int> ind = myBezier.getIndices();
vector<glm::vec3> verts = myBezier.getVertices();
vector<glm::vec2> tex = myBezier.getTexCoords();
vector<glm::vec3> norm = myBezier.getNormals();
for (int i = 0; i < myBezier.getNumIndices(); i++)
{
pvalues.push_back(verts[ind[i]].x);
pvalues.push_back(verts[ind[i]].y);
pvalues.push_back(verts[ind[i]].z);
tvalues.push_back(tex[ind[i]].s);
tvalues.push_back(tex[ind[i]].t);
nvalues.push_back(norm[ind[i]].x);
nvalues.push_back(norm[ind[i]].y);
nvalues.push_back(norm[ind[i]].z);
}
glGenVertexArrays(1, &VAO);
glGenBuffers(3, VBO);
glBindVertexArray(VAO);
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, nvalues.size() * 4, &nvalues[0], GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);
glBufferData(GL_ARRAY_BUFFER, tvalues.size() * 4, &tvalues[0], GL_STATIC_DRAW);
}
unsigned int loadTexture(char const* path)
{
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
int width, height, nrComponents;
stbi_set_flip_vertically_on_load(true);
unsigned char* data = stbi_load(path, &width, &height, &nrComponents, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else {
std::cout << "Texture failed to load at path: " << path << std::endl;
}
stbi_image_free(data);
return textureID;
}
在这一部分,你可以对你的项目结果适当的进行截图展示和说明。
Bezier曲面, 贴图天津大学校旗图案展示
通过调整相机远近, 可以看到完整模型与Bezier曲面
同时可以通过键盘鼠标操作旋转视角
在这一部分,你需要对你在开发过程中遇到的一些困难进行描述和你是如何攻克的,在方法上和结果上展开叙述。 第三章占整体篇幅的30%。
在实现Bezier曲面时,我面临了一些挑战。首先,我需要考虑如何加载和处理控制点。Bezier曲面通常由控制点定义,因此我需要找到一种有效的方式来加载这些控制点。我采取了以下措施来解决这些问题:
整合Bezier曲面和实验一的困难:
整合Bezier曲面和实验一是另一个具有挑战性的任务。最初,我发现两者无法同时显示出来,这可能是因为没有正确地绑定和解绑VAO导致的。为了解决这个问题,我采取了以下措施:
VAO和渲染状态:我确保了解OpenGL渲染管线的工作方式,以及如何正确地绑定和解绑VAO。通过及时的绑定和解绑VAO,我成功地整合了Bezier曲面和实验一,使它们能够同时显示。
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, myBezier.getNumIndices());
glBindVertexArray(0);
Shader程序:我确保两者之间的着色器程序设置和变量传递是正确的。在调试过程中,我检查了着色器程序是否编译和链接成功,并确保正确传递了变量和纹理。
OpenGL状态管理:我了解了OpenGL的状态管理,包括混合、深度测试、清除颜色缓冲等。在整合两者时,我确保了正确管理OpenGL状态,以避免不必要的问题。
坐标转换和变换:我也考虑到了物体的变换,如旋转、平移或缩放,以确保这些变换适用于Bezier曲面,使它们在同一坐标系中正确呈现。
通过逐步调试和测试,我克服了这些挑战,成功地将Bezier曲面整合到实验一中,以实现更复杂的图形应用程序。这个过程不仅增加了我的对OpenGL和图形编程的理解,还提高了我的解决问题和整合不同组件的能力。这一章的详细描述将有助于读者了解我在实验中面对的挑战以及我是如何成功应对它们。
课程收获,项目开发收获,课程建议等各个方面展开来叙述。内容上要有意义,量上要占整篇报告的10%左右。
在整个课程和项目开发过程中,我获得了许多宝贵的经验和知识,这些经验将对我产生深远的影响。以下是我在这个过程中的主要收获和一些建议:
课程收获:
项目开发收获:
课程建议:
总的来说,这门课程和项目开发经历为我提供了宝贵的机会,让我深入研究计算机图形学和图形编程。我相信我所获得的知识和技能将对我的学术和职业生涯产生长远的影响,同时也期待在未来的学术和职业发展中继续学习和成长。感谢这个精彩的学习经验!
文章作者: Alan Zeng
原始链接: https://alanzeng.com/blogs/17573/
版权说明:本博客所有文章除特别声明外,均采用BY-NC-SA 4.0许可协议。获得许可后,要求转载时注明文章出处和网站链接,谢谢!