OpenGL官方文档中的入门教程源代码:在3维空间中自由移动
- 项目总览:
- 一、开发前的准备工作
- 1.将以上链接中的三个文件分别放到自己硬盘的一个文件夹中:例如D盘/OpenGL/...
- 2.打开VS2022创建一个项目,右击窗体选择属性
- 3.配置这3个文件的一些环境到你的项目中,这里可以参考:[这节视频](https://www.bilibili.com/video/BV11W411N7b9/?spm_id_from=333.999.0.0&vd_source=ee4dc98d9036bcc310740dff78231cf7),同时这也是一节非常好的OpenGL课程,感谢傅老师!
- 二、代码目录
- 1.头文件Camera.h
- 2.源文件Camera.cpp
- 3.头文件Shader.h
- 4.源文件Shader.cpp
- 5.源文件main.cpp
- 三、着色器
- 1.顶点着色器vertexSource.txt
- 2.片段着色器fragmentSource.txt
- 四、效果展示:
项目总览:
1.开发语言:C++语言
2.IDE:VS2022
3.开发用时:一周
4.本项目是OpenGL官方文档https://learnopengl.com中《入门》栏目的案例,素材和函数功能都可在官方文档找到。
5.源码已上传到我的GitHub,链接:https://github.com/2394799692/OpenGLpractice1 或点此跳转
以下是本篇文章正文内容,欢迎朋友们进行指正,一起探讨,共同进步。——来自读研路上的lwj。QQ:2394799692(请标明来意)
一、开发前的准备工作
链接:https://pan.baidu.com/s/1dtXE22PZw14hif0ui3r2qA?pwd=z22g 提取码:z22g
1.将以上链接中的三个文件分别放到自己硬盘的一个文件夹中:例如D盘/OpenGL/…
2.打开VS2022创建一个项目,右击窗体选择属性
3.配置这3个文件的一些环境到你的项目中,这里可以参考:这节视频,同时这也是一节非常好的OpenGL课程,感谢傅老师!
二、代码目录
1.头文件Camera.h
定义源文件Camera.cpp中所使用到的函数和变量,这两个文件的作用是定义鼠标为摄像机的位置,从而实现在3维坐标中上下左右的移动
#pragma once
#include <glm.hpp>
#include <gtc/matrix_transform.hpp>
class Camera
{
public:
Camera(glm::vec3 position, glm::vec3 target, glm::vec3 worldup);
Camera(glm::vec3 position, float pitch, float yaw, glm::vec3 worldup);
glm::vec3 Position;
glm::vec3 Forward;
glm::vec3 Right;
glm::vec3 Up;
glm::vec3 WorldUp;
float Pitch;
float Yaw;
float SenseX = 0.001f;
float SenseY = 0.001f;
float speedZ = 0;
glm::mat4 GetViewMatrix();
void ProcessMouseMovement(float deltaX, float deltaY);
void UpdateCameraPos();
private:
void UpdateCameraVectors();
};
2.源文件Camera.cpp
#include "Camera.h"
Camera::Camera(glm::vec3 position, glm::vec3 target, glm::vec3 worldup)
{
Position = position;
WorldUp = worldup;
Forward = glm::normalize(target - position);//转为单位向量
Right = glm::normalize(glm::cross(Forward, WorldUp));//cross是叉乘的意思
Up = glm::normalize(glm::cross(Forward, Right));
}
Camera::Camera(glm::vec3 position, float pitch, float yaw, glm::vec3 worldup)
{
Position = position;
WorldUp = worldup;
Pitch = pitch;
Yaw = yaw;
Forward.x = glm::cos(Pitch) * glm::sin(Yaw);
Forward.y = glm::sin(Pitch);
Forward.z = glm::cos(Pitch) * glm::cos(Yaw);
Right = glm::normalize(glm::cross(Forward, WorldUp));//cross是叉乘的意思
Up = glm::normalize(glm::cross(Forward, Right));
}
glm::mat4 Camera::GetViewMatrix()
{
return glm::lookAt(Position, Position + Forward, WorldUp);
}
void Camera::UpdateCameraVectors()
{
Forward.x = glm::cos(Pitch) * glm::sin(Yaw);
Forward.y = glm::sin(Pitch);
Forward.z = glm::cos(Pitch) * glm::cos(Yaw);
Right = glm::normalize(glm::cross(Forward, WorldUp));//cross是叉乘的意思
Up = glm::normalize(glm::cross(Forward, Right));
}
void Camera::ProcessMouseMovement(float deltaX, float deltaY)
{
Pitch -= deltaY* SenseX;
Yaw -= deltaX* SenseY;
UpdateCameraVectors();
}
void Camera::UpdateCameraPos()
{
Position += Forward*speedZ*0.01f;
}
3.头文件Shader.h
结合shader.cpp和着色器资源实现渲染/着色功能
#pragma once
#include<string>
using namespace std;
class Shader
{
public:
Shader(const char* vertexPath,const char* fragmentPath);
string vertexString;
string fragmentString;
const char* vertexSource;
const char* fragmentSource;
unsigned int ID;//shader program id
void use();
private:
void checkCompileErrors(unsigned int ID, std::string type);
};
4.源文件Shader.cpp
#include "Shader.h"
#include<iostream>
#include<fstream>
#include<sstream>
#define GLEW_STATIC
#include<GL/glew.h>
#include<GLFW/glfw3.h>
using namespace std;
//shader命名空间下的shader方法
Shader::Shader(const char* vertexPath, const char* fragmentPath)
{
ifstream vertexFile;
ifstream fragmentFile;
stringstream vertexSStream;
stringstream fragmentSStream;
vertexFile.open(vertexPath);
fragmentFile.open(fragmentPath);
vertexFile.exceptions(ifstream::failbit || ifstream::badbit);
fragmentFile.exceptions(ifstream::failbit || ifstream::badbit);
try
{
if (!vertexFile.is_open() || !fragmentFile.is_open())
{
throw exception("open file error");
}
vertexSStream << vertexFile.rdbuf();
fragmentSStream << fragmentFile.rdbuf();
vertexString = vertexSStream.str();
fragmentString = fragmentSStream.str();
vertexSource = vertexString.c_str();
fragmentSource = fragmentString.c_str();
unsigned int vertex, fragment;//承载gpu返还的shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vertexSource, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERIEX");
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fragmentSource, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
glDeleteShader(vertex);
glDeleteShader(fragment);
}
catch (const std::exception& ex)
{
printf(ex.what());
}
}
void Shader::use() {
glUseProgram(ID);
}
void Shader::checkCompileErrors(unsigned int ID, string type)
{
int success;
char infoLog[512];
if (type != "PROGRAM")
{
glGetShaderiv(ID, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(ID, 512, NULL, infoLog);
cout << "shader compile error:" << infoLog << endl;
}
}
else
{
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(ID, 512, NULL, infoLog);
cout << "program link error:" << infoLog << endl;
}
}
}
5.源文件main.cpp
#include<iostream>
#define GLEW_STATIC
#include<GL/glew.h>
#include<GLFW/glfw3.h>
#include "Shader.h"
#include <glm.hpp>
#include <gtc/matrix_transform.hpp>
#include <gtc/type_ptr.hpp>
#include "Camera.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
//float vertices[] = {
// // ---- 位置 ---- ---- 颜色 ---- - 纹理坐标 -
// 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上
// 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下
// -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下
// -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上
//};
float vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};
unsigned int indices[] = {
0,1,2,
2,3,0
};
glm::vec3 cubePositions[] = {
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(2.0f, 5.0f, -15.0f),
glm::vec3(-1.5f, -2.2f, -2.5f),
glm::vec3(-3.8f, -2.0f, -12.3f),
glm::vec3(2.4f, -0.4f, -3.5f),
glm::vec3(-1.7f, 3.0f, -7.5f),
glm::vec3(1.3f, -2.0f, -2.5f),
glm::vec3(1.5f, 2.0f, -2.5f),
glm::vec3(1.5f, 0.2f, -1.5f),
glm::vec3(-1.3f, 1.0f, -1.5f)
};
float lastX;
float lastY;
bool firstMouse = true;
//引入class类
//Camera camera(glm::vec3(0,0,3.0f),glm::vec3(0,0,0),glm::vec3(0,1.0f,0));
Camera camera(glm::vec3(0, 0, 3.0f), glm::radians(15.0f), glm::radians(180.0f), glm::vec3(0, 1.0f, 0));
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
{
camera.speedZ=1.0f;
}
else if(glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
{
camera.speedZ = -1.0f;
}
else {
camera.speedZ = 0;
}
}
void mouse_callback(GLFWwindow* window, double xPos, double yPos)
{
if (firstMouse == true)
{
lastX = xPos;
lastY = yPos;
firstMouse = false;
}
float deltaX, deltaY;
deltaX = xPos - lastX;
deltaY = yPos - lastY;
lastX = xPos;
lastY = yPos;
camera.ProcessMouseMovement(deltaX, deltaY);
/*printf("%f \n", deltaX);*/
}
int main()
{
glfwInit();//初始化
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//打开一个窗体
GLFWwindow* window = glfwCreateWindow(800, 600, "My OpenGL Game", NULL, NULL);
if (window == NULL)
{
printf("open window failed.");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);//消失鼠标
glfwSetCursorPosCallback(window, mouse_callback);
//init glew
glewExperimental = true;
if (glewInit() != GLEW_OK)
{
printf("Init GLEW failed.");
glfwTerminate();
return -1;
}
glViewport(0, 0, 800, 600);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);//背面剔除,正面:GL_FRONT
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);//显示边框
glEnable(GL_DEPTH_TEST);//做深度缓冲
Shader* myShader = new Shader("vertexSource.txt", "fragmentSource.txt");
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
unsigned int EBO;
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
/*glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);*/
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);
unsigned int TexBufferA;
glGenTextures(1, &TexBufferA);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TexBufferA);
int width, height, nrChannel;
stbi_set_flip_vertically_on_load(true);//图像翻转
unsigned char* data = stbi_load("container.jpg", &width, &height, &nrChannel, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
printf("load image failed.");
}
stbi_image_free(data);
unsigned int TexBufferB;//载入笑脸
glGenTextures(1, &TexBufferB);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, TexBufferB);
unsigned char* data2 = stbi_load("awesomeface.png", &width, &height, &nrChannel, 0);
if (data2)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
printf("load image failed.");
}
stbi_image_free(data2);
glm::mat4 trans;
//trans = glm::translate(trans, glm::vec3(-0.01f, 0, 0));//平移
//trans = glm::rotate(trans,glm::radians(45.0f), glm::vec3(0, 0, 1.0f));//旋转45度
//trans = glm::scale(trans, glm::vec3(2.0f, 2.0f, 2.0f));//缩放
glm::mat4 modelMat;
modelMat = glm::rotate(modelMat, glm::radians(-45.0f), glm::vec3(0, 1.0f, 1.0f));
glm::mat4 viewMat;
//viewMat = glm::translate(viewMat, glm::vec3(0, 0, -3.0f));
//viewMat = camera.GetViewMatrix();
glm::mat4 projMat;
projMat = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f);
while (!glfwWindowShouldClose(window))
{
/*glm::mat4 trans;//定义在里面每次重置角度不会转太快
trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0));
trans = glm::rotate(trans, (float)glfwGetTime(), glm::vec3(0, 0, 1.0f));*/
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
/*glClear(GL_COLOR_BUFFER_BIT);*/
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//加了一个清除上一轮深度缓冲
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TexBufferA);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, TexBufferB);
glBindVertexArray(VAO);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
viewMat = camera.GetViewMatrix();
for (int i = 0; i < 10; i++) {
glm::mat4 modelMat2;
modelMat2 = glm::translate(modelMat2, cubePositions[i]);
myShader->use();
glUniform1i(glGetUniformLocation(myShader->ID, "ourTexture"), 0);
glUniform1i(glGetUniformLocation(myShader->ID, "ourFace"), 3);
//glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "transform"), 1, GL_FALSE, glm::value_ptr(trans));
glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "modelMat"), 1, GL_FALSE, glm::value_ptr(modelMat2));
glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "viewMat"), 1, GL_FALSE, glm::value_ptr(viewMat));
glUniformMatrix4fv(glGetUniformLocation(myShader->ID, "projMat"), 1, GL_FALSE, glm::value_ptr(projMat));
glDrawArrays(GL_TRIANGLES, 0, 36);
//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
glfwSwapBuffers(window);
glfwPollEvents();
camera.UpdateCameraPos();
}
glfwTerminate();
return 0;
}
三、着色器
1.顶点着色器vertexSource.txt
顶点着色器(Vertex Shader)是几个可编程着色器中的一个。如果我们打算做渲染的话,现代OpenGL需要我们至少设置一个顶点和一个片段着色器。
#version 330 core
layout (location=0) in vec3 aPos;
layout (location=1) in vec3 aColor;
layout (location=2) in vec2 aTexCoord;
//uniform mat4 transform;
uniform mat4 modelMat;
uniform mat4 viewMat;
uniform mat4 projMat;
out vec4 vertexColor;
out vec2 TexCoord;
void main(){
gl_Position=projMat*viewMat*modelMat*vec4(aPos.x,aPos.y,aPos.z,1.0f);
vertexColor=vec4(aColor.x,aColor.y,aColor.z,1.0f);
TexCoord=aTexCoord;
}
2.片段着色器fragmentSource.txt
片段着色器(Fragment Shader)是第二个也是最后一个我们打算创建的用于渲染三角形的着色器。片段着色器所做的是计算像素最后的颜色输出。
#version 330 core
in vec4 vertexColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
uniform sampler2D ourFace;
out vec4 FragColor;
void main(){
FragColor = mix(texture(ourTexture,TexCoord),texture(ourFace,TexCoord), 0.2);
//如果第三个值是0.0,它会返回第一个输入;如果是1.0,会返回第二个输入值。0.2会返回80%的第一个输入颜色和20%的第二个输入颜色,即返回两个纹理的混合色。
}
四、效果展示:
视频效果:
OpenGL入门demo
图片效果: