参考:LearnOpenGL
思路:使用3维向量,定义10个立方体的位置。使用for循环进行10个立方体的绘制,令每个立方体旋转角度不同,来达到更好的视觉效果。
举个栗子相对于上篇文章,我们仅修改了main.cpp,按照思路进行修改,但去除了立方体的旋转
代码
main.cpp
//头文件
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include "Shader.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
//函数声明
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
// 设置窗体宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
//主函数
int main()
{
// glfw: 初始化和配置
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw 窗体生成
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "More3D", NULL, NULL);//设置标题
//判断窗体是否生成
if (window == NULL)
{
std::cout << "生成GLFW窗口失败" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// glad: 加载所有的OpenGL功能指针----------------------------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "初始化GLAD失败" << std::endl;
return -1;
}
//开启深度测试
glEnable(GL_DEPTH_TEST);
// 建立并编译着色器--------------------------------------------------------------------
Shader* myShader = new Shader("vertexSource.txt", "fragmentSource.txt");
// 设置点数据 (还有缓冲) 配置点的属性(包含点坐标等) 这里设置了4个,将以索引的方式选择点来画三角形
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 indices[] = {
0, 1, 2, // 第一个三角形选择索引为 0 1 3的三个点
2, 3, 0, // 第一个三角形选择索引为 1 2 3的三个点
};
//10个立方体的坐标
glm::vec3 cubePositions[] = {
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(2.0f, 5.0f, -15.0f),
glm::vec3(-1.5f, -2.2f, -2.5f),
glm::vec3(-3.8f, -2.0f, -12.3f),
glm::vec3(2.4f, -0.4f, -3.5f),
glm::vec3(-1.7f, 3.0f, -7.5f),
glm::vec3(1.3f, -2.0f, -2.5f),
glm::vec3(1.5f, 2.0f, -2.5f),
glm::vec3(1.5f, 0.2f, -1.5f),
glm::vec3(-1.3f, 1.0f, -1.5f)
};
unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO); //注意,这里使用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(6, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(6);
//glVertexAttribPointer(7, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
//glEnableVertexAttribArray(7);
glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(8);
//纹理-------------------------------------------------------------------------------
unsigned int TexBufferA, TexBufferB;
stbi_set_flip_vertically_on_load(true);//y轴翻转
//木箱部分
glGenTextures(1, &TexBufferA);
//激活
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, TexBufferA);
/*glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);*/
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindTexture(GL_TEXTURE_2D, TexBufferA);//绑定
int width, height, nrChannels;
unsigned char *data = stbi_load("container.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 << "加载纹理失败" << std::endl;
}
stbi_image_free(data);//释放
//笑脸部分
glGenTextures(1, &TexBufferB);
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, TexBufferB);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindTexture(GL_TEXTURE_2D, TexBufferB);//绑定
unsigned char *data2 = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);//注意,有Alpha通道
glGenerateMipmap(GL_TEXTURE_2D);//生成多级渐远纹理
}
else
{
std::cout << "加载纹理失败" << std::endl;
}
stbi_image_free(data2);
//注意这是允许的,对glVertexAttribPointer的调用将VBO注册为顶点属性的绑定顶点缓冲区对象,所以之后我们可以安全地解除绑定
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 记住:当VAO处于活动状态时,不要取消绑定EBO,因为绑定元素缓冲对象IS存储在VAO中; 保持EBO的约束力。
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// 您可以在之后取消绑定VAO,以便其他VAO调用不会意外地修改此VAO,但这种情况很少发生。无论如何,
// 修改其他VAO需要调用glBindVertexArray,因此我们通常不会在不直接需要时解除VAO(VBO同样)的绑定。
glBindVertexArray(0);
// 取消注释此调用会绘制线框多边形。
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//glm相关------------------------------------------------------------------
// 下面就是矩阵初始化的一个例子,如果使用的是0.9.9及以上版本
// 下面这行代码就需要改为:
// glm::mat4 trans = glm::mat4(1.0f)
glm::mat4 trans;
//trans = glm::translate(trans, glm::vec3(1.0f, 0.0f, 0.0f));
//trans = glm::rotate(trans, glm::radians(45.0f), glm::vec3(0.0f, 0.0f, 1.0f));
//trans = glm::scale(trans, glm::vec3(1.30, 1.30, 1.30));
glm::mat4 modelMat;
modelMat = glm::rotate(modelMat, glm::radians(-55.0f), glm::vec3(1.0f, 0.0f, 0.0f));
glm::mat4 viewMat;
// 注意,我们将矩阵向我们要进行移动场景的反方向移动。
viewMat = glm::translate(viewMat, glm::vec3(0.0f, 0.0f, -3.0f));
glm::mat4 projMat;
projMat = glm::perspective(glm::radians(45.0f),800.0f / 600.0f,0.1f, 100.0f);//(透视投影,远小近大)
while (!glfwWindowShouldClose(window))
{
//随时间旋转
//modelMat = glm::rotate(modelMat, (float)glfwGetTime() * glm::radians(30.0f), glm::vec3(0.5f, 1.0f, 0.0f));
// 输入
processInput(window);
Sleep(200);
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, TexBufferA);
glActiveTexture(GL_TEXTURE3); // 在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, TexBufferB);
glBindVertexArray(VAO);
/*glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);*/
for (int i = 0;i < 10;++i)
{
glm::mat4 modelMat2;
modelMat2 = glm::translate(modelMat2, cubePositions[i]);
float angle = 20.0f * i;//每个旋转角度不同
modelMat2 = glm::rotate(modelMat2, glm::radians(angle), glm::vec3(1.0f, 0.3f, 0.5f));
glDrawArrays(GL_TRIANGLES, 0, 36);
//使用着色器
myShader->use();
glUniform1i(glGetUniformLocation(myShader->ID, "ourTexture"), 0); // 手动设置
//glUniform1i(glGetUniformLocation(myShader->ID, "ourFace"), 3); // 手动设置
myShader->setInt("ourFace", 3); // 或者使用着色器类设置
/*unsigned int transformLoc = glGetUniformLocation(myShader->ID, "transform");*/
/*glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));*/
unsigned int modelMatLoc = glGetUniformLocation(myShader->ID, "modelMat");
unsigned int viewMatLoc = glGetUniformLocation(myShader->ID, "viewMat");
unsigned int projMatLoc = glGetUniformLocation(myShader->ID, "projMat");
glUniformMatrix4fv(modelMatLoc, 1, GL_FALSE, glm::value_ptr(modelMat2));//使用modelMat的话就是一个,拿出for循环
glUniformMatrix4fv(viewMatLoc, 1, GL_FALSE, glm::value_ptr(viewMat));
glUniformMatrix4fv(projMatLoc, 1, GL_FALSE, glm::value_ptr(projMat));
}
// 画矩形--------------------------------------------------------------------------
//可以知道我们只有一个三角形VAO,没必要每次都绑定它,但是我们这么做会让代码有一点组织性
glBindVertexArray(VAO);
/*glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);*/
glDrawArrays(GL_TRIANGLES, 0, 36);
// glBindVertexArray(0); //没必要每次都解绑
// 交换buffers和poll的IO事件 (按键按下/释放,鼠标移动等.)
glfwSwapBuffers(window);
glfwPollEvents();
}
//一旦他们超出已有的资源,就取消所有资源的分配:
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
// glfw:终止,清空之前所有的GLFW的预分配资源
glfwTerminate();
return 0;
}
//查询GLFW相关按键是否被按下/释放,根据情况作出反应
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// g无论窗口大小何时改变(由操作系统或用户自己)这个回调函数将会被执行
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
//确定viewport与新的窗口尺寸匹配; 请注意,宽度和高度将明显大于显示器上指定的宽度和高度。
glViewport(0, 0, width, height);
}
截图
更多OpenGL知识:现代OpenGL入门教程
有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。