计算机图形学与opengl C++版 学习笔记 第1章 入门

写在前面:思前想后,还是更一下这篇opengl的学习笔记吧。虽然已经过去一年了 ,但是断断续续的都是用到哪里学哪里。感觉会了点又感觉不会很多。要想学会一件事一定要先过一遍,哪怕是基础呢。毕竟unity也是这样学会的。为了理解项目早点发顶会,希望读者与笔者一起加油,攻破opengl大关!

本篇教程基于《计算机图形学与opengl C++版》 侵权告知立即删除!


图形编程是基于着色器的 ——也就是说,有些程序是用诸如C++或Java等标准编程语言编写的,并运行在CPU上;而另一些是用专用的着色器语言编写的,并直接运行在显卡(GPU)上。

1.1 语言和库

现代图形编程使用图形库完成。现在有很多图形库,但常见的平台无关图形编程库叫作OpenGL(Open Graphics Library,开放图形库)。本书将会介绍如何在C++中使用OpenGL进行3D图形编程。

在C++中使用OpenGL需要配置多个库。总的来说,你需要以下这些语言和库:

  • C++开发环境;
  • OpenGL / GLSL;
  • 窗口管理库;
  • 扩展库;
  • 数学库;
  • 纹理管理库

1.1.1 C++

C++是一种通用编程语言,是需要高性能的系统的优秀选择,比如3D图形计算。C++的另一个优点是 OpenGL调用库是基于C语言开发的。本书windows用户请选择Visual Studio(我选择的是2017),Mac用户选择Xcode。

1.1.2 OpenGL/GLSL

OpenGL的1.0版本出现在1992年,是一种对供应商特定的计算机图形应用编程接口(API)的“开放性”替代。
这本书假定用户的机器有一个支持至少4.3版本OpenGL的显卡。

1.1.3 窗口管理库GLFW

OpenGL实际上并不是把图像直接绘制到计算机屏幕上,而是渲染到一个帧缓冲区,然后需要由这台机器来负责把帧缓冲区的内容绘制到屏幕上的一个窗口中。有不少库都可以支持这一部分工作。比如Microsoft Windows API。但这通常是不实用的,需要很多底层的编码工作。GLUT库曾经是一个很流行的选择,但现在已经被弃用了。它的一个现代化的演变是freeglut库。其他相关的选项还有CPW库、GLOW库和GLUI库

GLFW是最流行的选择之一,也是我们这本书中选择使用的。它内置了对Windows、macOS、Linux和其他操作系统[GF17]的支持

1.1.4 扩展库GLEW

现代版本的OpenGL,比如我们在本书中使用的4以上版本,需要识别GPU上可用的扩展。OpenGL核心中有一些内置的命令用来支持这些,但是为了使用每个现代命令,需要执行很多相当复杂的代码行。

在本书中,我们会持续不断地使用这些命令。所以使用一个扩展库来处理这些细节已经成了标准做法,这样能让程序员可以直接使用现代OpenGL命令。常用的是GLEW,它支持各种操作系统,包括 Windows、Macintosh和Linux [GE17]。

1.1.5 数学库GLM

3D图形编程大量使用向量和矩阵代数。常常和OpenGL一起使用的两个这样的库是Eigen和vmath。因此,配合一个支持常见数学计算任务的函数库或者类包,能极大地方便OpenGL的使用。

本书中使用的,是OpenGL Mathematics,一般称作GLM。它是一个只有头文件的C++库,兼容 Windows、macOS和Linux [GM17]。 GLM提供与图形概念相关的类和基本数学函数,例如矢量、矩阵和四元数。它还包含各种工具类,用于创建和使用常见的3D图形结构, 例如透视和视角矩阵。

1.1.6 纹理管理库SOIL2

从第5章开始,我们将使用图像文件来向我们图形场景中的对象添 加“纹理”。这意味着我们会需要频繁加载这些图像文件到我们的C++/ OpenGL代码中。使用一个纹理加载库通常是更好的。比如FreeImage、DevIL、OpenGL Image (GLI)和Glraw。

本书中使用的纹理图像加载库是SOIL2——SOIL的一个更新的分叉版本。像我们之前选择的库一样,SOIL2兼容各种平台[SO17]。附录中给出了详细的安装和配置说明。

1.1.7 可选库

读者可能希望利用很多其他有用的库。例如,在本书中,我们将展示如何从零开始实现一个简单的“OBJ”模型加载器。然而,正如我们将看到的,它没有处理OBJ标准中可用的很多选项。有一些更复杂的现成的OBJ加载器可供选择,比如Assimp和tinyobjloader。在我们的例子中,我们会只用在本书中介绍和实现的简单模型加载器。

1.2 安装和配置

参考之前记录的博客:基于Visual Studio 2017的opengl安装

通用文件

Utils.cpp

#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
#include "Utils.h"
using namespace std;

Utils::Utils() {}
//读取glsl文件 
//filePath glsl文件路径
//返回:一行glsl文本内容
string Utils::readShaderFile(const char *filePath) {
	string content;
	ifstream fileStream(filePath, ios::in);
	string line = "";
	while (!fileStream.eof()) {
		getline(fileStream, line);
		content.append(line + "\n");
	}
	fileStream.close();
	return content;
}
//检测并捕获opengl在编译和运行期间的错误
bool Utils::checkOpenGLError() {
	bool foundError = false;
	int glErr = glGetError();
	while (glErr != GL_NO_ERROR) {
		cout << "glError: " << glErr << endl;
		foundError = true;
		glErr = glGetError();
	}
	return foundError;
}
// 捕获编译着色器时的错误
void Utils::printShaderLog(GLuint shader) {
	int len = 0;
	int chWrittn = 0;
	char *log;
	glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
	if (len > 0) {
		log = (char *)malloc(len);
		glGetShaderInfoLog(shader, len, &chWrittn, log);
		cout << "Shader Info Log: " << log << endl;
		free(log);
	}
}
//创建、载入并编译着色器
//shaderTYPE 着色器名字枚举型GL_VERTEX_SHADER GL_GEOMETRY_SHADER GL_FRAGMENT_SHADER
//shaderPath 着色器glsl文件
//返回:编译完的着色器对象
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;
}
// 捕获链接着色器时的错误
void Utils::printProgramLog(int prog) {
	int len = 0;
	int chWrittn = 0;
	char *log;
	glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len);
	if (len > 0) {
		log = (char *)malloc(len);
		glGetProgramInfoLog(prog, len, &chWrittn, log);
		cout << "Program Info Log: " << log << endl;
		free(log);
	}
}

int Utils::finalizeShaderProgram(GLuint sprogram)
{
	GLint linked;
	glLinkProgram(sprogram);
	checkOpenGLError();
	glGetProgramiv(sprogram, GL_LINK_STATUS, &linked);
	if (linked != 1)
	{
		cout << "linking failed" << endl;
		printProgramLog(sprogram);
	}
	return sprogram;
}
//两个着色器加入程序对象 
//vp 顶点着色器的glsl文件
//fp 片段着色器的glsl文件
//返回:程序对象
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::createShaderProgram(const char *vp, const char *gp, const char *fp) {
	GLuint vShader = prepareShader(GL_VERTEX_SHADER, vp);
	GLuint gShader = prepareShader(GL_GEOMETRY_SHADER, gp);
	GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fp);
	GLuint vgfprogram = glCreateProgram();
	glAttachShader(vgfprogram, vShader);
	glAttachShader(vgfprogram, gShader);
	glAttachShader(vgfprogram, fShader);
	finalizeShaderProgram(vgfprogram);
	return vgfprogram;
}

GLuint Utils::createShaderProgram(const char *vp, const char *tCS, const char* tES, const char *fp) {
	GLuint vShader = prepareShader(GL_VERTEX_SHADER, vp);
	GLuint tcShader = prepareShader(GL_TESS_CONTROL_SHADER, tCS);
	GLuint teShader = prepareShader(GL_TESS_EVALUATION_SHADER, tES);
	GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fp);
	GLuint vtfprogram = glCreateProgram();
	glAttachShader(vtfprogram, vShader);
	glAttachShader(vtfprogram, tcShader);
	glAttachShader(vtfprogram, teShader);
	glAttachShader(vtfprogram, fShader);
	finalizeShaderProgram(vtfprogram);
	return vtfprogram;
}

GLuint Utils::createShaderProgram(const char *vp, const char *tCS, const char* tES, char *gp, const char *fp) {
	GLuint vShader = prepareShader(GL_VERTEX_SHADER, vp);
	GLuint tcShader = prepareShader(GL_TESS_CONTROL_SHADER, tCS);
	GLuint teShader = prepareShader(GL_TESS_EVALUATION_SHADER, tES);
	GLuint gShader = prepareShader(GL_GEOMETRY_SHADER, gp);
	GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fp);
	GLuint vtgfprogram = glCreateProgram();
	glAttachShader(vtgfprogram, vShader);
	glAttachShader(vtgfprogram, tcShader);
	glAttachShader(vtgfprogram, teShader);
	glAttachShader(vtgfprogram, gShader);
	glAttachShader(vtgfprogram, fShader);
	finalizeShaderProgram(vtgfprogram);
	return vtgfprogram;
}

GLuint Utils::loadCubeMap(const char *mapDir) {
	GLuint textureRef;
	string xp = mapDir; xp = xp + "/xp.jpg";
	string xn = mapDir; xn = xn + "/xn.jpg";
	string yp = mapDir; yp = yp + "/yp.jpg";
	string yn = mapDir; yn = yn + "/yn.jpg";
	string zp = mapDir; zp = zp + "/zp.jpg";
	string zn = mapDir; zn = zn + "/zn.jpg";
	textureRef = SOIL_load_OGL_cubemap(xp.c_str(), xn.c_str(), yp.c_str(), yn.c_str(), zp.c_str(), zn.c_str(),
		SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS);
	if (textureRef == 0) cout << "didnt find cube map image file" << endl;
	//	glBindTexture(GL_TEXTURE_CUBE_MAP, textureRef);
	// reduce seams
	//	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	//	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	//	glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
	return textureRef;
}

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;
}

// GOLD material - ambient, diffuse, specular, and shininess
float* Utils::goldAmbient() { static float a[4] = { 0.2473f, 0.1995f, 0.0745f, 1 }; return (float*)a; }
float* Utils::goldDiffuse() { static float a[4] = { 0.7516f, 0.6065f, 0.2265f, 1 }; return (float*)a; }
float* Utils::goldSpecular() { static float a[4] = { 0.6283f, 0.5559f, 0.3661f, 1 }; return (float*)a; }
float Utils::goldShininess() { return 51.2f; }

// SILVER material - ambient, diffuse, specular, and shininess
float* Utils::silverAmbient() { static float a[4] = { 0.1923f, 0.1923f, 0.1923f, 1 }; return (float*)a; }
float* Utils::silverDiffuse() { static float a[4] = { 0.5075f, 0.5075f, 0.5075f, 1 }; return (float*)a; }
float* Utils::silverSpecular() { static float a[4] = { 0.5083f, 0.5083f, 0.5083f, 1 }; return (float*)a; }
float Utils::silverShininess() { return 51.2f; }

// BRONZE material - ambient, diffuse, specular, and shininess
float* Utils::bronzeAmbient() { static float a[4] = { 0.2125f, 0.1275f, 0.0540f, 1 }; return (float*)a; }
float* Utils::bronzeDiffuse() { static float a[4] = { 0.7140f, 0.4284f, 0.1814f, 1 }; return (float*)a; }
float* Utils::bronzeSpecular() { static float a[4] = { 0.3936f, 0.2719f, 0.1667f, 1 }; return (float*)a; }
float Utils::bronzeShininess() { return 25.6f; }

Utils.h

#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <SOIL2\soil2.h>
#include <string>
#include <iostream>
#include <fstream>
#include <cmath>
#include <vector>
#include <glm\glm.hpp>
#include <glm\gtc\type_ptr.hpp>
#include <glm\gtc\matrix_transform.hpp>

class Utils
{
private:
	static std::string readShaderFile(const char *filePath);
	static void printShaderLog(GLuint shader);
	static void printProgramLog(int prog);
	static GLuint prepareShader(int shaderTYPE, const char *shaderPath);
	static int finalizeShaderProgram(GLuint sprogram);

public:
	Utils();
	static bool checkOpenGLError();
	static GLuint createShaderProgram(const char *vp, const char *fp);
	static GLuint createShaderProgram(const char *vp, const char *gp, const char *fp);
	static GLuint createShaderProgram(const char *vp, const char *tCS, const char* tES, const char *fp);
	static GLuint createShaderProgram(const char *vp, const char *tCS, const char* tES, char *gp, const char *fp);
	static GLuint loadTexture(const char *texImagePath);
	static GLuint loadCubeMap(const char *mapDir);

	static float* goldAmbient();
	static float* goldDiffuse();
	static float* goldSpecular();
	static float goldShininess();

	static float* silverAmbient();
	static float* silverDiffuse();
	static float* silverSpecular();
	static float silverShininess();

	static float* bronzeAmbient();
	static float* bronzeDiffuse();
	static float* bronzeSpecular();
	static float bronzeShininess();
};

使用笔者的opengl环境

我的项目已上传至此处。内含本课程所有源码及笔者注释,以及一些小的开源项目。

https://github.com/Doggerlas/OpenGL_Proj

使用方法:
1.下载压缩包至本地,解压后用vs2017打开OpenGL_Project.sln工程文件
2.进入vs2017后,重定此解决方案,并把模式调成Release x86即可使用ctrl+F5完美运行。

说明:本项目包含一个OpenGL_Proj_template的项目模板,新建项目可直接使用。此模板包含了3rdparty内的头文件相对路径和库文件相对路径以及库名称。所以即使你的电脑没有配置opengl,也可直接在此项目中运行给定的或者自己的opengl程序。(注意*.vcxproj路径下要有所需的*glsl *obj *jpg dll等依赖文件,源码.cpp *.h可在vs内指定文件位置,不必放在.vcxproj路径下)

在这里插入图片描述
若要新增项目 建议自己配置项目模板
1.新建一个空项目。
2.点击视图-属性。

  • VC++/包含目录下新增“…\3rdparty\include”;
    在这里插入图片描述

  • 链接器/常规/附加库目录新增““…\3rdparty\lib”;
    在这里插入图片描述

  • 链接器/输入/附加依赖项更改
    kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);glew32.lib;glfw3.lib;soil2-debug.lib;opengl32.lib
    在这里插入图片描述

3.点击项目-导出模板即可。这样每次新增项目选择模板后都会沿用这套配置属性。(不会导出请看上一篇博客)
在这里插入图片描述

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
课程解决的问题: 作为游戏行业或者图形学从业者,你是否面临以下问题: 到底openGL底层如何实现的? 到底矩阵操作变换是怎么做到的? 到底光栅化的算法以及原理是什么? 到底如何才能从3D世界投射到2D屏幕呢? 图形学有这么多的矩阵操作,到底如何推导如何应用呢? 学完这门课程,你应该就可以从底层了解一个初级的openGL图形接口如何实现,图形学最底层的封装到底面临哪些挑战;跟随我们一行一行写完代码,你就会得到一个迷你本的openGL图形库,你可以深度体会图形从模型变换,观察矩阵变换,投影矩阵变换一直到光栅化纹理操作的全套模拟流程。 课程介绍: 本课程将带领学员不使用任何图形库,实现从0到1的图形学接口封装以及算法讲解,并且带领大家手敲代码,一行一行进行实现。 涵盖了(环境搭建,绘制点,Bresenham算法绘制完美直线,三角形拆分绘制算法,颜色插值算法,图片操作,图片二次插值放缩算法,纹理系统接口搭建及封装,矩阵操作理论以及实践,openGL类似接口封装,3D世界的图形学理论及接口封装等) 最终将带领大家通过C++实现一个3D世界的图形接口,方便所有人入门图形学,进行接下来的openGL接口以及GPU编程的学习   本课程为系列课程的第一步入门,且带领所有人进行实现,更加实用,可以让大家打牢图形学的基础知识及编程技能

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值