1 #include <iostream>
2 using namespace std;
3 //using std::cout; using std::cin; using std::endl;
4
5 #define GLEW_STATIC
6 #include <GL/glew.h>
7 #include <GLFW/glfw3.h>
8
9 //#define STB_IMAGE_STATIC
10 //#define STR_IMAGE_IMPLEMENTATION
11 #include "stb_image.h"
12
13 //#include "stb_image.h"
14
15 void framebuffer_size_callback(GLFWwindow* window, int width, int height);
16 void processInput(GLFWwindow* window);
17
18 //着色器源码
19 //明确表示我们使用核心模式,使用in关键字,在顶点着色器中声明所有的输入顶点属性
20 //由于每个顶点都有一个3D坐标,我们就创建一个vec3输入变量aPos;
21 //用layout(location = 0)设定了输入变量的位置值
22 const char *vertexShaderSource = "#version 330 core\n"
23 "layout (location = 0) in vec3 aPos;\n"
24 "layout (location = 1) in vec3 aColor;\n"
25 "layout (location = 2) in vec2 aTexCoord;\n"
26
27 "out vec3 ourColor;\n"
28 "out vec2 TexCoord;\n"
29
30 "void main()\n"
31 "{\n"
32 //aPos.w用在所谓的透视除法上
33 //我们将gl_Postion设置的值会成为该顶点着色器的输出,由于我们的输入是一个3分量的向量,我们必须把它转换成4分量的
34 " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
35 " ourColor = aColor;\n"
36 " TexCoord = aTexCoord\n;"
37 "}\n\0";
38
39 //片段着色器源码
40 const char* fragmentShaderSource = "#version 330 core\n"
41 "out vec4 FragColor;\n"
42 "in vec3 ourColor;\n"
43 "in vec2 TexCoord;\n"
44
45 //"uniform sampler2D ourTexture;\n" //采样器
46 "uniform sampler2D texture1;\n"
47 "uniform sampler2D texture2;\n"
48 "void main()\n"
49 "{\n"
50 //最终输出的颜色是两个纹理的结合
51 //" FragColor = texture(ourTexture, TexCoord);\n"
52 " FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.7);\n"
53 "}\n\0";
54
55
56 int main()
57 {
58 glfwInit(); //初始化glfw
59 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); //配置glfw,主版本号
60 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); //次版本号
61 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //核心模式
62 GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpengl", NULL, NULL); //创建glfw窗口
63 if (window == NULL)
64 {
65 cout << "Fail to create window!\n" << endl;
66 glfwTerminate();
67 return -1;
68 }
69
70 //设置当前窗口的上下文
71 glfwMakeContextCurrent(window); //context环境、上下文
72
73 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); //每次改变窗口大小时都调用这个函数
74
75 glewExperimental = GL_TRUE; //使得glew在管理opengl函数指针时更多使用现代化技术
76
77 if (glewInit() != GLEW_OK)
78 {
79 cout << "Fail to initialize GLEW" << endl;
80 return -1;
81 }
82
83 //顶点着色器对象
84 unsigned int vertexShader; //用id来引导创建一个着色器对象
85 vertexShader = glCreateShader(GL_VERTEX_SHADER); //我们把需要创建的的着色器类型以参数形式提供给glCreateShader
86
87 //把这个着色器源码附加到着色器对象上
88 //glShaderSource函数吧需要编译的着色器对象作为第一个参数,第二个参数指定了传递的源码字符串数量,这里只有一个
89 //第三个参数是顶点着色器真正的源码,第四个参数我们先设置为NULL
90 glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
91
92 //编译这个着色器源码
93 glCompileShader(vertexShader);
94
95 int success; //定义一个整型变量来表示是否成功编译
96 char infoLog[512]; //定义一个储存错误消息的容器
97 //用glGetShaderiv检查是否编译成功
98 glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
99
100
101 if (!success) //如果编译失败,用glGetShaderInfoLog获取错误消息,然后打印它
102 {
103 glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
104 cout << "ERROR R::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << endl;
105 }
106
107 //片段着色器对象
108 unsigned int fragmentShader; //定义一个片段着色器对象
109 fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); //给对象赋予GL_FRAGMENT_SHADER类型
110 glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); //将片段着色器的源码绑定到对象上
111 glCompileShader(fragmentShader); //编译该对象
112 glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); //检查是否编译成功
113
114 if (!success)
115 {
116 glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); //若不成功,获取错误信息,存入infoLog
117 cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << endl;
118 }
119
120 //着色器对象
121 unsigned int shaderProgram; //定义一个用来链接的着色器程序中
122 shaderProgram = glCreateProgram(); //给对象赋一个实体
123
124 glAttachShader(shaderProgram, vertexShader); //将Vertex附加到程序对象上
125 glAttachShader(shaderProgram, fragmentShader); //将fragment着色器附加到程序对象上
126 glLinkProgram(shaderProgram); //用glLinkProgram来连接他们
127
128 glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); //检查是否链接成功
129 if (!success)
130 {
131 glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); //若不成功,获取错误信息,存入infoLog
132 cout << "ERROR::PROGRAM::SHADER::LINK_FAILED\n" << infoLog << endl;
133 }
134
135 glDeleteShader(vertexShader); //删除顶点着色器对象,我们不再需要他了
136 glDeleteShader(fragmentShader); //删除片段着色器对象,我们也不再需要他了
137
138
139 GLfloat vertices[] = { //输入3个顶点坐标
140 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
141 0.5f, -0.5f, 0.0f,0.0f, 1.0f, 0.0f, 1.0f, 0.0f, //位置,颜色,纹理坐标
142 -0.5f, -0.5f, 0.0f,0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
143 -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f
144
145 };
146
147
148 unsigned int indices[] = { //主义索引从0开始
149 0, 1, 3, //第一个三角形
150 1, 2, 3 //第二个三角形
151 };
152
153 unsigned int VBO, VAO, EBO; //定义一个顶点缓冲变量
154 glGenVertexArrays(1, &VAO); //创建一个VAO对象
155 glGenBuffers(1, &VBO); //利用该函数和一个缓冲id生成一个VBO对象
156 glGenBuffers(1, &EBO); //创建一个EBO对象
157
158 glBindVertexArray(VAO); //绑定VAO
159 glBindBuffer(GL_ARRAY_BUFFER, VBO); //顶点缓冲对象的缓冲类型为GL_ARRAY_BUFFER, 把新创建的缓冲绑定到GL_ARRAY_BUFFER上
160 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); //索引缓冲对象的缓冲类型为GL_ELEMENT_ARRAY_BUFFER, 把新创建的缓冲绑定到GL_ELEMENT_ARRAY_BUFFER上
161
162 //glBufferData是一个专门用来把用户定义的数据复制到当前绑定缓冲的的函数
163 //它的第一个参数是目标缓冲类型,顶点缓冲对象当前绑定到GL_ARRAY_BUFFER上
164 //第二个参数指定传输数据的大小,用sizeof函数计算
165 //第三个参数是我们希望发送的实际数据
166 //第四个参数指定了我们希望显卡如何管理给定的数据:GL_STATIC_DRAW / GL_DYNAMIC_DRAW / GL_STREAM_DRAW
167 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
168 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//将EBO对象复制到缓冲区
169
170 //链接顶点属性
171 //第一个参数表示顶点位置其实位置值,第二个表示每个顶点的维度,第三个参数表示每个数据的数据类型
172 //第四个参数表示是否希望数据被成标准化,如果是GL_TRUE的话,有符号被映射到(-1, 1),无符号映射到(0, 1)
173 //第五个参数表示一个顶点(所有维度相加)的数据大小,也称为连续顶点属性组之间的间隔,步长
174 //第6个参数表示位置数据在缓冲中起始位置的偏移量(offset),由于位置数据再数组的开头,所以这里是0
175 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);//顶点位置属性
176 glEnableVertexAttribArray(0); //启用顶点位置属性,顶点属性默认是禁用的
177
178
179 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));//顶点颜色属性
180 glEnableVertexAttribArray(1); //启用顶点颜色属性
181
182 glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); //纹理属性
183 glEnableVertexAttribArray(2); //启用纹理属性
184
185 unsigned int texture1, texture2;
186
187 //创建一个纹理对象,第一个参数是生成纹理的数量,然后把它们存储在第二个参数的unsigned in数组中(我们的例子只是一个单独的unsigned int)
188 glGenTextures(1, &texture1);
189 //glGenTextures(1, &texture2);
190
191 //glActiveTexture(GL_TEXTURE0); //在绑定纹理之前先激活纹理单元
192 glBindTexture(GL_TEXTURE_2D, texture1); //绑定该纹理对象,让之后任何的纹理指令都可以配置当前绑定的纹理
193 //glActiveTexture(GL_TEXTURE1);
194 //glBindTexture(GL_TEXTURE_2D, texture2);
195
196
197 //为当前绑定的纹理对象设置环绕方式
198 //
199 //纹理图像环绕设置
200 //第一个参数为纹理目标,我们使用的是2D纹理,所以纹理目标为GL_TEXTURE_2D
201 //第二个参数需要我们指定设置的选项和应用的纹理轴,这里我们是WRAP选项,S 和T轴
202 //最后一个参数需要我们传递一个环绕方式
203 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
204 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
205
206 ////如果我们选择GL_CLAMP_TO_BOREDER选项,我们还需要指定一个边缘颜色
207 //float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
208 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
209
210
211 //为当前绑定的纹理对象设置过滤方式
212 //缩小时用临近过滤,放大时用线性过滤
213 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
214 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
215
216 ////多级渐远纹理过滤
217 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//在两个临近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样
218 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
219
220 int width, height, nrChannels;
221
222 stbi_set_flip_vertically_on_load(true);
223 //用stbi_load函数加载图片,加载并生成纹理
224 unsigned char *data = stbi_load("timg.jpg", &width, &height, &nrChannels, 0);
225
226 if (data)
227 {
228 //用glTexImage函数来生成纹理,调用之后,当前绑定的纹理对象就会被附加上纹理图像
229 //第一个参数指定了纹理目标,设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理
230 //第二个参数为纹理指定多级渐远纹理的级别,0就是基本级别
231 //第三个参数告诉OpenGL我们希望吧纹理存储为何种格式,我们的图像只有RGB值,因此我们也把纹理存储为RGB值
232 //第四个和第五个参数设置最终纹理的宽度和高度
233 //下个参数应该总是被设为0
234 //第七和第八参数定义了源图的格式和数据类型,我们使用RGB值加载这个图像,并把他们存储为char(byte)数组,我们将会传入对应值
235 //最后一个参数是真正的图像数据
236 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
237 glGenerateMipmap(GL_TEXTURE_2D);
238
239 //然而,目前只有基本级别(base-level)的纹理图像被加载了,如果要使用多级渐远纹理,我们必须手动设置所有不同的图像(不断递增
240 //第二个参数),或者,直接在生成纹理之后调用glGenerateMipmap,这会为当前绑定的纹理自动生成所有需要的多级渐远纹理
241 }
242 else
243 {
244 cout << "Failed to load texture1\n" << endl;
245 }
246
247 //生成了纹理和相应的多级渐远纹理后,释放图像的内存是一个很好的习惯
248 stbi_image_free(data);
249
250 glGenTextures(1, &texture2);
251 glBindTexture(GL_TEXTURE_2D, texture2);
252 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
253 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
254 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
255 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
256
257 data = stbi_load("wall.jpg", &width, &height, &nrChannels, 0);
258 if (data)
259 {
260 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
261 glGenerateMipmap(GL_TEXTURE_2D);
262 }
263 else
264 {
265 cout << "Failed to load texture2!" << endl;
266 }
267
268 stbi_image_free(data);
269
270 glUseProgram(shaderProgram);
271 glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);
272 glUniform1i(glGetUniformLocation(shaderProgram, "texture2"), 1);
273
274
275
276 glBindBuffer(GL_ARRAY_BUFFER, 0);
277
278 glBindVertexArray(0);
279
280
281 while (!glfwWindowShouldClose(window))
282 {
283 processInput(window);
284
285 //render
286 glClearColor(0.2f, 0.4f, 0.5f, 1.0f);
287 glClear(GL_COLOR_BUFFER_BIT);
288
289 glActiveTexture(GL_TEXTURE0);
290 glBindTexture(GL_TEXTURE_2D, texture1);
291 glActiveTexture(GL_TEXTURE1);
292 glBindTexture(GL_TEXTURE_2D, texture2);
293
294 //glBindTexture(GL_TEXTURE_2D, texture);
295 //画第一个三角形
296 glUseProgram(shaderProgram); //激活这个程序对象
297
298
299 glBindVertexArray(VAO);
300
301 //glDrawArrays函数第一个参数是我们打算绘制Opengl图元的类型,这里是三角形GL_TRIANGLES
302 //第二个参数制定了顶点数组的起始索引,这里填0
303 //第三个参数指定我们打算绘制多少个顶点
304 //lDrawArrays(GL_TRIANGLES, 0, 3); //
305
306
307 //绘制这个矩形
308 //glDrawElements第一个参数指定了我们绘制的图元模式
309 //第二个参数是需绘制的顶点个数
310 //第三个参数是索引类型,这里是GL_UNSIGNED_INT
311 //最后一个参数是我们指定EBO中的偏移量(或者传递一个索引数组)
312 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
313
314 glfwSwapBuffers(window); //交换颜色缓冲,用来绘制,显示在屏幕上
315 glfwPollEvents(); //检查是否触发事件、更新窗口状态
316 }
317
318 glDeleteVertexArrays(1, &VAO); //释放顶点数组对象
319 glDeleteBuffers(1, &VBO); //释放顶点缓冲对象
320 glDeleteBuffers(1, &EBO);
321
322 glfwTerminate(); //释放之前分配的资源
323
324 return 0;
325 }
326
327 void framebuffer_size_callback(GLFWwindow* window, int width, int height)
328 {
329 glViewport(0, 0, width, height);
330 }
331
332 void processInput(GLFWwindow* window)
333 {
334 if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
335 glfwSetWindowShouldClose(window, true);
336 }