先上一波效果图:
实际上就是:画了一个矩形,然后贴了两张图,下面是一个木窗,上面一个笑脸。
首先放上这次教程所需要的贴图和库文件的百度云
链接:https://pan.baidu.com/s/1Ejn65QoYW11cDukiC6ZFjg
提取码:hl93
(1)添加需要的库文件
我们本次教程的流程,用到了本地资源中的图片,读取和使用本地图片,需要使用一个新的库:stb_image.h
这里我已经下载好了,你们可以直接下载我的百度云,找到需要的头文件,然后加到自己的项目目录中。
(2)编写需要的shader类:这里的shader类和我们上一节的教程中是一样的
#ifndef SHADER_H
#define SHADER_H
#include <glad/glad.h> // 包含glad来获取所有的必须OpenGL头文件
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class Shader
{
public:
// 程序ID
unsigned int ID;
// 构造器读取并构建着色器
Shader(const GLchar* vertexPath, const GLchar* fragmentPath);
// 使用/激活程序
void use();
// uniform工具函数
void setBool(const std::string &name, bool value) const;
void setInt(const std::string &name, int value) const;
void setFloat(const std::string &name, float value) const;
private:
void checkCompileErrors(unsigned int shader, std::string type);
};
#endif
#include "shader_s.h"
Shader::Shader(const GLchar * vertexPath, const GLchar * fragmentPath)
{
// 1. 从文件路径中获取顶点/片段着色器
std::string vertexCode;
std::string fragmentCode;
std::ifstream vShaderFile;
std::ifstream fShaderFile;
// 保证ifstream对象可以抛出异常:
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
// 打开文件
vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream;
// 读取文件的缓冲内容到数据流中
vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf();
// 关闭文件处理器
vShaderFile.close();
fShaderFile.close();
// 转换数据流到string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
const char* vShaderCode = vertexCode.c_str();
const char * fShaderCode = fragmentCode.c_str();
// 2. 编译着色器
unsigned int vertex, fragment;
// 顶点着色器 vs
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
checkCompileErrors(vertex, "VERTEX");
// 片段着色器 fs
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
checkCompileErrors(fragment, "FRAGMENT");
// 着色器程序
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");
// 删除着色器,它们已经链接到我们的程序中了,已经不再需要了 glDeleteShader(vertex);
glDeleteShader(fragment);
}
void Shader::use()
{
glUseProgram(ID);
}
void Shader::setBool(const std::string & name, bool value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value);
}
void Shader::setInt(const std::string & name, int value) const
{
glUniform1i(glGetUniformLocation(ID, name.c_str()), value);
}
void Shader::setFloat(const std::string & name, float value) const
{
glUniform1f(glGetUniformLocation(ID, name.c_str()), value);
}
void Shader::checkCompileErrors(unsigned int shader, std::string type)
{
int success;
char infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
else
{
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
(3)编写shader脚本,这里有三个版本,大家可以直接放上(三)然后写完主程序后,再调整(一)和(二)
文件目录可以像我这样创建:
(一)仅放一张贴图
a)texture.vs顶点着色器:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
}
b)texture.vs片段着色器:
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
void main()
{
FragColor = texture(ourTexture, TexCoord);
}
此时我们的运行结果是这样:
(二)第一张贴图的基础上再加上渐变色(上一个教程的三色渐变)
修改的地方是我们的片段着色器
texture.fs
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
void main()
{
//我们只需把纹理颜色与顶点颜色在片段着色器中相乘来混合二者的颜色:
FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0);
}
此时效果图如下:
(三)贴两张纹理
顶点着色器texture.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
}
片段着色器texture.fs
0.0
1.0
//0.2
80%
20%
最终输出颜色现在是两个纹理的结合:
(4)编写主程序:
关键代码的注释我已经翻译并且加上去,一些常规代码可以忽略或者把英文注释翻一下吧
1 #include <glad/glad.h>
2 #include <GLFW/glfw3.h>
3
4 #include "stb_image.h"
5 #include "shader_s.h"
6 #include <iostream>
7
8 void framebuffer_size_callback(GLFWwindow* window, int width, int height);
9 void processInput(GLFWwindow *window);
10
11 // settings
12 const unsigned int SCR_WIDTH = 800;
13 const unsigned int SCR_HEIGHT = 600;
14
15 int main()
16 {
17 // glfw: initialize and configure
18 // ------------------------------
19 glfwInit();
20 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
21 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
22 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
23
24 #ifdef __APPLE__
25 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
26 #endif
27
28 // glfw window creation
29 // --------------------
30 GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
31 if (window == NULL)
32 {
33 std::cout << "Failed to create GLFW window" << std::endl;
34 glfwTerminate();
35 return -1;
36 }
37 glfwMakeContextCurrent(window);
38 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
39
40 // glad: load all OpenGL function pointers
41 // ---------------------------------------
42 if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
43 {
44 std::cout << "Failed to initialize GLAD" << std::endl;
45 return -1;
46 }
47
48 // build and compile our shader zprogram
49 // ------------------------------------
50 Shader ourShader("../res/textures/texture.vs", "../res/textures/texture.fs");
51
52 // set up vertex data (and buffer(s)) and configure vertex attributes
53 // ------------------------------------------------------------------
54 float vertices[] = {
55 // 位置信息 // 颜色信息 // 纹理 coords
56 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上
57 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下
58 -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下
59 -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上
60 };
61 unsigned int indices[] = {
62 0, 1, 3, // 第一个三角形
63 1, 2, 3 // 第二个三角形
64 };
65 unsigned int VBO, VAO, EBO;
66 glGenVertexArrays(1, &VAO);
67 glGenBuffers(1, &VBO);
68 glGenBuffers(1, &EBO);
69
70 glBindVertexArray(VAO);
71
72 glBindBuffer(GL_ARRAY_BUFFER, VBO);
73 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
74
75 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
76 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
77
78 // position attribute
79 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
80 glEnableVertexAttribArray(0);
81 // color attribute
82 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
83 glEnableVertexAttribArray(1);
84 // texture coord attribute
85 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
86 glEnableVertexAttribArray(2);
87
88
89 // 加载并创建纹理
90 // -------------------------
91 unsigned int texture1, texture2;
92 // 第一张纹理
93
94 glGenTextures(1, &texture1);
95 glBindTexture(GL_TEXTURE_2D, texture1);
96 // 为当前绑定的纹理对象设置环绕、过滤方式
97 // 将纹理包装设置为GL_REPEAT(默认包装方法)
98 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
99 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
100 // 设置纹理过滤参数
101 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
102 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
103 // 加载并生成纹理
104 int width, height, nrChannels;
105 stbi_set_flip_vertically_on_load(true); //告诉stb_image.h在y轴上翻转加载的纹理。
106
107 unsigned char *data = stbi_load("../res/textures/container.jpg", &width, &height, &nrChannels, 0);
108 if (data)
109 {
110 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
111 glGenerateMipmap(GL_TEXTURE_2D);
112 }
113 else
114 {
115 std::cout << "Failed to load texture" << std::endl;
116 }
117 stbi_image_free(data);
118
119
120 // texture 2
121 glGenTextures(1, &texture2);
122 glBindTexture(GL_TEXTURE_2D, texture2);
123 // set the texture wrapping parameters
124 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
125 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
126 // set texture filtering parameters
127 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
128 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
129 // load image, create texture and generate mipmaps
130 data = stbi_load("../res/textures/awesomeface.png", &width, &height, &nrChannels, 0);
131 if (data)
132 {
133 //请注意,awesomeface.png具有透明度,因此具有alpha通道,
134 //因此请务必告诉OpenGL数据类型为GL_RGBA
135 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
136 glGenerateMipmap(GL_TEXTURE_2D);
137 }
138 else
139 {
140 std::cout << "Failed to load texture" << std::endl;
141 }
142 stbi_image_free(data);
143
144 //告诉每个采样器的opengl它属于哪个纹理单元(只需要做一次)
145 ourShader.use(); //激活着色器
146 // either set it manually like so:
147 glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);
148 // or set it via the texture class
149 ourShader.setInt("texture2", 1);
150
151
152
153 // render loop
154 // -----------
155 while (!glfwWindowShouldClose(window))
156 {
157 // input
158 // -----
159 processInput(window);
160
161 // render
162 // ------
163 glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
164 glClear(GL_COLOR_BUFFER_BIT);
165
166 // bind textures on corresponding texture units
167 glActiveTexture(GL_TEXTURE0);
168 glBindTexture(GL_TEXTURE_2D, texture1);
169 glActiveTexture(GL_TEXTURE1);
170 glBindTexture(GL_TEXTURE_2D, texture2);
171
172 // render container
173 ourShader.use();
174 glBindVertexArray(VAO);
175 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
176
177 // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
178 // -------------------------------------------------------------------------------
179 glfwSwapBuffers(window);
180 glfwPollEvents();
181 }
182
183 // optional: de-allocate all resources once they've outlived their purpose:
184 // ------------------------------------------------------------------------
185 glDeleteVertexArrays(1, &VAO);
186 glDeleteBuffers(1, &VBO);
187 glDeleteBuffers(1, &EBO);
188
189 // glfw: terminate, clearing all previously allocated GLFW resources.
190 // ------------------------------------------------------------------
191 glfwTerminate();
192 return 0;
193 }
194
195 // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
196 // ---------------------------------------------------------------------------------------------------------
197 void processInput(GLFWwindow *window)
198 {
199 if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
200 glfwSetWindowShouldClose(window, true);
201 }
202
203 // glfw: whenever the window size changed (by OS or user resize) this callback function executes
204 // ---------------------------------------------------------------------------------------------
205 void framebuffer_size_callback(GLFWwindow* window, int width, int height)
206 {
207 // make sure the viewport matches the new window dimensions; note that width and
208 // height will be significantly larger than specified on retina displays.
209 glViewport(0, 0, width, height);
210 }