前一节内容,如果读者能够坚持看到最后,并且能够将立方体画出来,我相信还是有一点点的成就感的,毕竟我们从点、到三角形、再到立方体、再到立方体的旋转一步一步的走,而且学会了VBO、VAO、EBO的数据传输、shader的使用等等最最基本的知识,这一步好艰难,但是毕竟走过来了。
那么下面我们将继续征途,本小节的内容,相对短一点,而且很容易明白。
本小节的主要内容,就是为前一小节的立方体,贴上一张图,使用纹理。
那么在opengl中我们如何去准备一张图、准备一张什么格式的图、以及把这个图放在程序的什么位置、以及怎么加载这个图、以及怎么将这个图传递给shader、是传递给shader的顶点着色器还是片段着色器、传递的这种图有什么用,怎么使用。
首先,我们来回到最后一个问题,这张图到底有什么用,为什么要使用纹理。
上一节中,我们的三角形,在光栅化之后,变为具体的一个一个像素,每个像素我们都是用了唯一的颜色进行赋值,那么如果我们想让颜色丰富起来,比如显示一张树叶,一条狗,那么是不是要对每个像素的颜色都要赋值呢?是的,你必须对每个像素点进行赋值一个颜色,这个工作量不是一般的大,于是我们就想能不能拍一张树叶的图、或者一条狗的图,用手机、相机、或者psp、或者直接从网上下载一个图片就能方便的实现贴图效果,答案是可以的。哦,到现在明白了,为什么要使用纹理,纹理就是让颜色丰富起来,让现实中的一个图贴到一个物体上去,就是刷漆,就是上色的效果。
问题又来了,我们使用这个图片的哪个位置的像素去贴到光栅化之后的每个像素点呢?是不是要有个纹理坐标呢?比如下面的三角形和一张纹理的对应情况:
于是我们修改之前的数据,变化后为:
#include
#include
#include
#include
#include
#include
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
using namespace std;
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec2 inTexcoord;\n"
"out vec2 outTexcoord;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos,1.0);\n"
" outTexcoord = vec2(inTexcoord.x, inTexcoord.y);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec2 outTexcoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
" FragColor = texture(texture1, outTexcoord);\n"
"}\n\0";
int main()
{
//初始化和创建窗口
glfwInit();
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
//设置
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
//三角形的三个顶点数据
float vertices[] = {
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.0f, 0.5f, 0.0f, 0.5f, 1.0f,
};
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
int width, height, nrChannels;
unsigned char *data = stbi_load("leaf.jpg", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
glUseProgram(shaderProgram);
glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(window);
glfwPollEvents();
}
return 0;
}
运行结果为:
我们看到上图显示的图片并不太对,于是我们第一个想法是找网址上https://learnopengl.com/#!Getting-started/Textures
是如何把图片显示清楚的,我们第一个做的就是把图片换成:
https://learnopengl.com/img/textures/wall.jpg
结果运行之后得到了如下的图:
结果就这样正确了,我的理解是我们刚才的图片的分辨率不够,这个图的分辨率为512*512。
那么此时我们要做的实验是,把图片缩小一点看看还能不能正常显示。
比如我们将图改为50*50的大小,如何把图片改为50*50呢,方法很简单,直接使用画图工具即可。
这样再改小图片之后,我们的程序运行结果为:
这样就出现了上面的问题,也就证明了我们的猜测。让我们结束这个反人类的三角形,画一个矩形试试,代码如下:
#include
#include
#include
#include
#include
#include
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
using namespace std;
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec2 inTexcoord;\n"
"out vec2 outTexcoord;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos,1.0);\n"
" outTexcoord = vec2(inTexcoord.x, inTexcoord.y);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec2 outTexcoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
" FragColor = texture(texture1, outTexcoord);\n"
"}\n\0";
int main()
{
//初始化和创建窗口
glfwInit();
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
//设置
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
//三角形的三个顶点数据
float vertices[] = {
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
};
unsigned int indices[] = {
0,1,2,
0,2,3,
};
unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
stbi_set_flip_vertically_on_load(true);
int width, height, nrChannels;
unsigned char *data = stbi_load("timg.jpg", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
glUseProgram(shaderProgram);
glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
//glDrawArrays(GL_TRIANGLES, 0, 3);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
return 0;
}
注意这里的索引数组是unsigned int类型的,开始的时候,我写成了float类型,结果在glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);却是unsigned int类型,所以导致画不出来。
运行结果如下:
下面我们接着做的就是,将立方体添加纹理,让立方体的六个面贴上纹理。我们除了将顶点的数据,换回原来的立方体数据,还要在while中改变画图的方式为: glDrawArrays(GL_TRIANGLES, 0, 36);
但是这样画出的立方体我们说过了,还是一个矩形,没有立体的感觉,只能看到一个面,所以还要讲立方体旋转起来,ok,所以还要改变顶点着色器的代码。把之前的让立方体的旋转代码粘贴过来。
代码如下:
#include
#include
#include
#include
#include
#include
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
using namespace std;
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec2 inTexcoord;\n"
"out vec2 outTexcoord;\n"
"uniform mat4 transform;\n"
"void main()\n"
"{\n"
" gl_Position = transform * vec4(aPos,1.0);\n"
" outTexcoord = vec2(inTexcoord.x, inTexcoord.y);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec2 outTexcoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
" FragColor = texture(texture1, outTexcoord);\n"
"}\n\0";
int main()
{
//初始化和创建窗口
glfwInit();
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
//设置
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
//三角形的三个顶点数据
float vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
stbi_set_flip_vertically_on_load(true);
int width, height, nrChannels;
unsigned char *data = stbi_load("timg.jpg", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
glUseProgram(shaderProgram);
glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glm::mat4 transform;
transform = glm::rotate(transform, 10 * (float)glfwGetTime(), glm::vec3(1.0f, 1.0f, 0.0f));
unsigned int transformLoc = glGetUniformLocation(shaderProgram, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glfwSwapBuffers(window);
glfwPollEvents();
}
return 0;
}
运行的结果如下:
你可能觉得上面的效果非常的奇怪,是的,非常奇怪,奇怪的地方有两点1、透了,近处的面看不到,远处的面居然看到了;2、没有透视的效果,即没有近大远小的效果。
我们来看看原来网址中的关于第1点的解释,你们可能也看不太懂,所以我还是给你们讲解一下,当然也是忠实原文的意思,opengl通过画三角形的方式来画立方体,但是没有任何的参数来限制先画的三角形是否被后画的三角形覆盖。所以当画完离我们近的三角形之后,再画一个远的三角形,这样后画的三角形就会被覆盖了。
那么opengl是怎么解决这个问题的呢,是通过一个深度值来判断是否要覆盖之前的像素,也就是传说中的z-buffer。opengl把每个像素点的z轴存放在深度缓冲中,当我们画一个像素点的时候,如果这个点的在之前相同位置点的z之后,那么则丢弃这个点,这个过程就是所谓的深度测试。
opengl把每个点的深度存储起来,自动的,完全不用我们操作。而深度测试,也不需要我们做,我们只需要启用深度测试即可。一句代码搞定,任性的很,不错你看到的就是任性二字。
glEnable(GL_DEPTH_TEST);
我们是将这句代码加到while循环中,还是非while循环中呢?
都可以,但是只需要设置一次即可,所以只需要放在非while中即可。
那么此时我们允许代码会发现,呀!什么都没有了,我们的立方体去哪了呢?幸好,原文中紧接着讲到,我们要清楚深度缓存,要在while循环中清理上一帧的深度信息,之前我们的清除,只是清除颜色缓冲,这里要再加一个深度清除。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
这样之后,我们的立方体就正常显示了。
#include
#include
#include
#include
#include
#include
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
using namespace std;
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec2 inTexcoord;\n"
"out vec2 outTexcoord;\n"
"uniform mat4 transform;\n"
"void main()\n"
"{\n"
" gl_Position = transform * vec4(aPos,1.0);\n"
" outTexcoord = vec2(inTexcoord.x, inTexcoord.y);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec2 outTexcoord;\n"
"uniform sampler2D texture1;\n"
"void main()\n"
"{\n"
" FragColor = texture(texture1, outTexcoord);\n"
"}\n\0";
int main()
{
//初始化和创建窗口
glfwInit();
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
//设置
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
//三角形的三个顶点数据
float vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
unsigned int texture1;
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
stbi_set_flip_vertically_on_load(true);
int width, height, nrChannels;
unsigned char *data = stbi_load("timg.jpg", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
glUseProgram(shaderProgram);
glUniform1i(glGetUniformLocation(shaderProgram, "texture1"), 0);
glEnable(GL_DEPTH_TEST);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glm::mat4 transform;
transform = glm::rotate(transform, 10 * (float)glfwGetTime(), glm::vec3(1.0f, 1.0f, 0.0f));
unsigned int transformLoc = glGetUniformLocation(shaderProgram, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glfwSwapBuffers(window);
glfwPollEvents();
}
return 0;
}
运行结果为:
yep,我们看到了正常的立方体了。
那么下面我们的立方体能不能多弄几个立方体呢?我们要做的只需要平移一个立方体即可。讲到这里,我们似乎还是对坐标系统不太熟悉,什么是本地坐标、什么是世界坐标、什么是视口坐标、什么是裁剪空间、什么又是透视除法、什么又是ndc空间,搞这么多概念到底又有啥作用?哎……一股凉风再次袭来,不禁看到好难、好抽象呀,没事慢慢听我道来。