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 }