1 #include2 using namespace std; 3 //using std::cout; using std::cin; using std::endl; 4 5 #define GLEW_STATIC 6 #include 7 #include 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); //若不成功,获取错误信息,存入infoLog117 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); //若不成功,获取错误信息,存入infoLog132 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.0f144 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); //绑定VAO159 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_DRAW167 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),由于位置数据再数组的开头,所以这里是0175 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_2D201 //第二个参数需要我们指定设置的选项和应用的纹理轴,这里我们是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 //下个参数应该总是被设为0234 //第七和第八参数定义了源图的格式和数据类型,我们使用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 else243 {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 else264 {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 //render286 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_TRIANGLES302 //第二个参数制定了顶点数组的起始索引,这里填0303 //第三个参数指定我们打算绘制多少个顶点304 //lDrawArrays(GL_TRIANGLES, 0, 3); //305 306 307 //绘制这个矩形308 //glDrawElements第一个参数指定了我们绘制的图元模式309 //第二个参数是需绘制的顶点个数310 //第三个参数是索引类型,这里是GL_UNSIGNED_INT311 //最后一个参数是我们指定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 }