天天看點

變換

更多還是矩陣向量的基礎内容:

向量:方向和小大。

點積:

隻定義了兩個向量的夾角。你也許記得90度的餘弦值是0,0度的餘弦值是1。使用點乘可以很容易測試兩個向量是否正交(Orthogonal)或平行。

叉乘:

它需要兩個不平行向量作為輸入,生成一個正交于兩個輸入向量的第三個向量。如果輸入的兩個向量也是正交的,那麼叉乘之後将會産生3個互相正交的向量。

縮放:

矩陣的組合:

改着色器,添加變化矩陣,用到uniform

// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
#include <iostream>
#include "Shader.h"
#include <SOIL.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

void key_callback(GLFWwindow* windows, int key, int scancode, int action, int mode);


//頂點着色器源碼:

const GLuint WIDTH = 800, HEIGHT = 600;
float  Offset = 0.1;
int main()
{
  /*
  //此部分寫變換
  glm::vec4 vec(1.0f, 0.0f, 0.0f, 1.0f);
  glm::mat4 trans;
  trans = glm::translate(trans, glm::vec3(1.0f, 1.0f, 0.0f));
  vec = trans * vec;
  std::cout << vec.x << vec.y << vec.z << std::endl;
  */


  //然後是旋轉和縮放箱子
  glm::mat4 trans;
  trans = glm::rotate(trans, glm::radians(90.0f), glm::vec3(0.0, 0.0, 1.0));
  trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
  //因為我們把這個矩陣傳遞給了GLM的每個函數,GLM會自動将矩陣相乘
  //傳回的結果是一個包括了多個變換的變換矩陣。
  //轉換弧度glm::radians(90.0f)将角度轉換為弧度。


  std::cout << "Starting GLFW context, OpenGL 3.3" << std::endl;

  //這都是初始化GLFW,後面的要看文檔
  glfwInit();
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);


  //建立視窗,
  GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
  if (window == nullptr)
  {
    std::cout << "Failed to create GLFW window" << std::endl;
    glfwTerminate();
    return -1;
  }
  glfwMakeContextCurrent(window);//通知GLFW将我們視窗的上下文設定為目前線程的主上下文

  

  //不知道回調函數為什麼要加在這裡??
  //glfwSetKeyCallback(window, key_callback2);回調函數隻有最下面那個有作用
  glfwSetKeyCallback(window, key_callback);


  //初始化GLEW(管理OpenGL的函數指針)
  glewExperimental = GL_TRUE;
  if (glewInit() != GLEW_OK)
  {
    std::cout << "Failed to initialize GLEW" << std::endl;
    return -1;
  }

  
  glViewport(0, 0, WIDTH, HEIGHT);//前兩個控制左下角的位置,後面是視窗寬度高度(像素)

  Shader ourShader("shader.vs", "shader.frag");
 
  GLfloat vertices[] = {
    //位置                      //顔色
    //----位置----       ----顔色---- - 紋理坐标 -
    0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上
    0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下
    -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下
    -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
  };

  GLuint indices[] = {  // Note that we start from 0!
    0, 1, 3, // First Triangle
    1, 2, 3  // Second Triangle
  };

  


  GLuint VBO, VAO,EBO;
  //使用glGenBuffers函數和一個緩沖ID生成一個VBO對象??
  glGenBuffers(1, &VBO);
  glGenVertexArrays(1, &VAO);
  glGenBuffers(1, &EBO);

  //綁定VAO
  glBindVertexArray(VAO);
  //複制頂點數組到緩沖中,glBufferData是一個專門用來把使用者定義的資料複制到目前綁定緩沖的函數
  glBindBuffer(GL_ARRAY_BUFFER, VBO);//glBindBuffer函數把新建立的緩沖綁定到GL_ARRAY_BUFFER目标上
  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, 8 * sizeof(GLfloat), (GLvoid*)0);
  glEnableVertexAttribArray(0);
  
  //因為現在是兩個屬性,需要向右移動步長,6個
  glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
  glEnableVertexAttribArray(1);
  //由于我們添加了額外的頂點屬性,我們要告訴OPenGL新的頂點格式。
  glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
  glEnableVertexAttribArray(2);
  glBindVertexArray(0);
  //安全?
  
  
  
  //生成紋理
  GLuint texture1,texture2;
  glGenTextures(1, &texture1);//??
  //glGenTextures(1, &texture2);
  //glGenTextures函數首先需要輸入生成紋理的數量,然後儲存在GLuint數組中,讓之前任何紋理指令都可以配置目前綁定的紋理。
  //下面這一句是綁定紋理。
  //glActiveTexture(GL_TEXTURE0);//多個位置時,使用前需要先激活
  glBindTexture(GL_TEXTURE_2D, texture1);
  //glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);

  //同理設定第二個紋理位置
  //glActiveTexture(GL_TEXTURE1);//多個位置時,使用前需要先激活
  //glBindTexture(GL_TEXTURE_2D, texture2);
  //glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);



  //設定到兩個軸上
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);  // Set texture wrapping to GL_REPEAT (usually basic wrapping method)
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
  //上面兩句是對S和T軸設定環繞方式,環繞方式為重複紋理圖像。
  float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
  glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);


  //下面生成紋理。2D紋理,是以紋理目标是GL_TEXTURE_2D
  //下面兩句設定方法和縮小的紋理過濾方式
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

  //使用SOIL加載圖檔,自此到循環前都是加載并生成紋理。
  int width, height;
  std::cout << SOIL_last_result() << std::endl;
  unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
  std::cout << SOIL_last_result() << std::endl;
  if (image)
  {
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    //第一個參數紋理目标Target,會生成與目前綁定紋理對象同樣紋理
    //設定紋理指定多級漸遠紋理級别,0,基本級别
    //第三個參數,紋理儲存為何種格式,RGB
    //四五是設定最終的紋理的寬度和高度,就用之前的變量。
    //0
    //七八位源圖的格式和資料類型,使用RGB加載并儲存為char(byte)數組
    //最後參數為圖像的資料
  }
  else
  {
    std::cout << "Failed to load texture." << std::endl;
  }

  //目前綁定的紋理對象會被附加上紋理圖像,然而隻有0級别的紋理圖像被加載 了,多級的話需要手動設定
  //或者可以,在生成紋理之後調用glGenerateMipmap,這會為目前綁定的紋理自動生成所有需要的多級漸遠紋理。
  glGenerateMipmap(GL_TEXTURE_2D);
  SOIL_free_image_data(image);
  glBindTexture(GL_TEXTURE_2D, 0);





  glGenTextures(1, &texture2);
  glBindTexture(GL_TEXTURE_2D, texture2);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
  // Set texture filtering
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  image = SOIL_load_image("awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
  glGenerateMipmap(GL_TEXTURE_2D);
  SOIL_free_image_data(image);
  glBindTexture(GL_TEXTURE_2D, 0);

  //防止退出的循環
  while (!glfwWindowShouldClose(window))
  {
    glfwPollEvents();


    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    //glClearColor是一個狀态設定函數,glClear是一個狀态應用函數

    
    //繪圖
    ourShader.Use();

    glUniform1f(glGetUniformLocation(ourShader.Program, "para"), Offset);


    //把變換矩陣傳遞給着色器
    GLuint transformLoc = glGetUniformLocation(ourShader.Program, "transform");
    glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
    //參數一是uniform的值,第二是矩陣個數,3,列主序矩陣不許置換4,變化一下資料


    glActiveTexture(GL_TEXTURE0);//這句是激活。
    glBindTexture(GL_TEXTURE_2D, texture1);
    glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texture2);
    glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);

    //這裡因為要使用第二個紋理,改變一點渲染流程,先綁定兩個紋理到對應的紋理單元,然後定義哪個uniform采樣器應對哪個紋理單元。


    glBindVertexArray(VAO);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);

    glfwSwapBuffers(window);
  }

  //回調函數
  
  //釋放:
  glDeleteVertexArrays(1, &VAO);
  glDeleteBuffers(1, &VBO);
  glDeleteBuffers(1, &EBO);

  glfwTerminate(); //釋放/删除之前的配置設定的所有資源
  return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{

  std::cout << key << std::endl;
  //使用者按下ESC鍵,我們設定window視窗WindowShouldClose屬性為true
  //關閉應用程式
  if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
    glfwSetWindowShouldClose(window, GL_TRUE);
  if (key == GLFW_KEY_UP&&action == GLFW_PRESS)
  {
    Offset += 0.1;
    if (Offset >= 1.0f) Offset = 1.0f;
  }
  if (key == GLFW_KEY_DOWN&&action == GLFW_PRESS)
  {
    Offset -= 0.1;
    if (Offset <= 0.0f) Offset = 0.0f;
  }
}      

着色器:

#version 330 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
layout(location = 2) in vec2 texCoord;
out vec3 ourColor;
out vec2 TexCoord;


uniform mat4 transform;

void main()
{
  gl_Position = transform * vec4(position, 1.0f);
  ourColor = color;
  TexCoord = vec2(1.0f-texCoord.x, 1.0f-texCoord.y);
}      

然後讓他根據開始運作時間旋轉,每秒轉50度。

在遊戲循環裡加上代碼:

glm::mat4 trans;
trans = glm::translate(trans, glm::vec3(0.5f, -0.5f, 0.0f));
trans = glm::rotate(trans, glm::radians((GLfloat)glfwGetTime()*50.0f), glm::vec3(0.0f, 0.0f, 1.0f));
//盡管在代碼中我們先位移再旋轉,實際的變換卻是先應用旋轉再是位移的。
//glfwGetTime(); 擷取一個從glfw初始化完到目前的一個時間秒數      

練習:

1.使用應用在箱子上的最後一個變換,嘗試将其改變為先旋轉,後位移。看看發生了什麼,試着想想為什麼會發生這樣的事情:

Remember that matrix multiplication is applied in reverse. This time a translation is thus

applied first to the container positioning it in the bottom-right corner of the screen.

After the translation the rotation is applied to the translated container.

A rotation transformation is also known as a change-of-basis transformation

for when we dig a bit deeper into linear algebra. Since we're changing the

basis of the container, the next resulting translations will translate the container

based on the new basis vectors. Once the vector is slightly rotated, the vertical

translations would also be slightly translated for example.

If we would first apply rotations then they'd resolve around the rotation origin (0,0,0), but 

since the container is first translated, its rotation origin is no longer (0,0,0) making it

looks as if its circling around the origin of the scene.

這塊确實挺複雜的需要好好想想。

2.嘗試再次調用glDrawElements畫出第二個箱子,隻使用變換将其擺放在不同的位置。讓這個箱子被擺放在視窗的左上角,并且會不斷的縮放(而不是旋轉)。sin函數在這裡會很有用,不過注意使用sin函數時應用負值會導緻物體被翻轉。

變換
glm::mat4 transform = glm::mat4();
transform = glm::translate(transform, glm::vec3(-0.5f, 0.5f, 0.0f));
GLfloat scaleAmount = sin(glfwGetTime());
transform = glm::scale(transform, glm::vec3(scaleAmount, scaleAmount, scaleAmount));
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transform));
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);