天天看點

openGL結合光照與紋理

openGL系列文章目錄

文章目錄

  • ​​openGL系列文章目錄​​
  • ​​前言​​
  • ​​一、實作思路​​
  • ​​二、代碼​​
  • ​​1.c++主程式​​
  • ​​2.頂點着色器​​
  • ​​3.片元着色器​​
  • ​​運作效果​​
  • ​​參考​​
  • ​​源碼下載下傳​​

前言

,在光照模型中,都是假設我們使用按ADS 定義的光源,照亮按ADS 定義材質的物體。某些對象的表面可能會指定紋理圖像。是以,我們需要一種方法來結合采樣紋理所得的顔色和光照模型産生的顔色。

一、實作思路

我們結合光照和紋理的方式取決于物體的特性以及其紋理的目的。這裡有多種情況,其

中常見的有:

􀀠 紋理圖像很寫實地反映了物體真實的表面外觀;

􀀠 物體同時具有材質和紋理;

􀀠 材質包括了陰影和反射資訊

􀀠 有多種光和/或多個紋理。

我們先來觀察第一種情景,物體擁有一個簡單的紋理,同時我們對它進行光照。實作這種光照的一種簡單方法是在片段着色器中完全将材質特性去除掉,之後使用紋理取樣所得紋理顔色代替材質的ADS 值。下面的僞代碼展示了這種政策:

fragColor = textureColor * ( ambientLight + diffuseLight ) + specularLight      

這種政策下,紋理顔色影響了環境光和漫反射分量,而鏡面反射顔色僅由光源決定。鏡面反射分量僅由光源決定是一種很常見的做法,尤其是對于金屬或“閃亮”的表面。但是,對于不那麼閃亮的表面,如織物或未上漆的木材(甚至一小部分金屬,如黃金),其鏡面高光部分都應當包含物體表明顔色。在這些情況下,之前的政策應該做适當微調:

fragColor = textureColor * ( ambientLight + diffuseLight + specularLight )      

同時也有一些情況下,物體本身具有ADS 材質,并伴有紋理圖像。如銀質物體使用紋理為表面添加一些氧化痕迹。在這些情況下,如之前章節中所講過的,既用到光照又用到材質的标準ADS 模型就可以與紋理顔色相結合,并權重求和。如:

textureColor = texture(sampler, texCoord)
lightColor = (ambLight * ambMaterial) + (diffLight * diffMaterial) + specLight
fragColor = 0.5 * textureColor + 0.5 * lightColor      

這種政策結合了光照、材質、紋理,并能夠擴充到多個光源以及多種材質的情況。如:

texture1Color = texture(sampler1, texCoord)
texture2Color = texture(sampler2, texCoord)
light1Color = (ambLight1 * ambMaterial) + (diffLight1 * diffMaterial) + specLight1
light2Color = (ambLight2 * ambMaterial) + (diffLight2 * diffMaterial) + specLight2
fragColor = 0.25 * texture1Color
+ 0.25 * texture2Color
+ 0.25 * light1Color
+ 0.25 * light2Color      

二、代碼

1.c++主程式

#include "glew/glew.h"
#include "glfw/glfw3.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
#include "Utils.h"
#include "ImportedModel.h"
#include "camera.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

static const int screen_width = 1920;
static const int screen_height = 1080;

static const int numVAOs = 1;
static const int numVBOs = 3;

static const float pai = 3.1415926f;

GLuint renderingProgram = 0;
GLuint vao[numVAOs] = { 0 };
GLuint vbo[numVBOs] = { 0 };

int width = 0;
int height = 0;
float aspect = 0.f;
float objLocX = 0.f, objLocY = 0.f, objLocZ = 0.f;
GLuint mvLoc = 0, projLoc = 0, nLoc = 0;
float amt = 0.f;

GLuint globalAmbLoc = 0, ambLoc = 0, diffLoc = 0, specLoc = 0, posLoc = 0, mAmbLoc = 0, mDiffLoc = 0, mSpecLoc = 0, mShiLoc = 0;


GLuint shuttleTextureId = 0;

glm::mat4 mMat(1.f), vMat(1.f), pMat(1.f), mvMat(1.f), invTrMat(1.f), rMat(1.f);


Camera camera(glm::vec3(0.f, 0.f, 2.f));
float cameraX, cameraY, cameraZ;
ImportedModel myModel("resources/shuttle.obj");

//光照相關
glm::vec3 currentLightPos(0.f), transformed(1.f);
float lightPos[3] = { 0.f };
glm::vec3 lightLoc = glm::vec3(5.f, 2.f, 2.f);

// white light
float globalAmbient[4] = { 0.7f, 0.7f, 0.7f, 1.f };
float lightAmbient[4] = { 0.f, 0.f, 0.f, 1.f };
float lightDiffuse[4] = { 1.f };
float lightSpecular[4] = { 1.f };

// gold material
float* matAmbient = Utils::goldAmbient();
float* matDiffuse = Utils::goldDiffuse();
float* matSpecular = Utils::goldSpecular();
float matShininess = Utils::goldShininess();

GLboolean keys[1024] = { GL_FALSE };
GLboolean firstMouse = GL_TRUE;
float deltaTime = 0.f;
float lastFrame = 0.f;
float lastLocX = 0.f;
float lastLocY = 0.f;

float toRadians(float degrees)
{
  return (degrees * 2.f * pai) / 360.f;
}

void setupVertices(void)
{
  vector<glm::vec3> vert = myModel.getVertices();
  vector<glm::vec2> text = myModel.getTextureCoords();
  vector<glm::vec3> norm = myModel.getNormals();

  vector<float> pValues;
  vector<float> tValues;
  vector<float> nValues;

  for (int i=0; i< myModel.getNumVertices(); i++)
  {
    pValues.push_back(vert[i].x);
    pValues.push_back(vert[i].y);
    pValues.push_back(vert[i].z);

    tValues.push_back(text[i].s);
    tValues.push_back(text[i].t);

    nValues.push_back(norm[i].x);
    nValues.push_back(norm[i].y);
    nValues.push_back(norm[i].z);
  }

  glGenVertexArrays(numVAOs, vao);
  glBindVertexArray(vao[0]);

  glGenBuffers(numVBOs, vbo);
  glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
  glBufferData(GL_ARRAY_BUFFER, pValues.size() * sizeof(float), &(pValues[0]), GL_STATIC_DRAW);
  //glBufferData(GL_ARRAY_BUFFER, myModel.getVertices().size() * sizeof(float), &(pVlaues[0]), GL_STATIC_DRAW);

  glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
  glBufferData(GL_ARRAY_BUFFER, tValues.size() * sizeof(float), &(tValues[0]), GL_STATIC_DRAW);
  //glBufferData(GL_ARRAY_BUFFER, myModel.getTextureCoords().size() * sizeof(float), &(tValues[0]), GL_STATIC_DRAW);

  glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
  glBufferData(GL_ARRAY_BUFFER, nValues.size() * sizeof(float), &(nValues[0]), GL_STATIC_DRAW);
  //glBufferData(GL_ARRAY_BUFFER, myModel.getNormals().size() * sizeof(float), &(nValues[0]), GL_STATIC_DRAW);
}

void init(GLFWwindow* window)
{
  renderingProgram = Utils::createShaderProgram("vertShader.vert", "fragShader.frag");
  //glfwGetWindowSize(window, &width, &height);
  cameraX = 0.0f; cameraY = 0.0f; cameraZ = 2.f;
  objLocX = 0.0f; objLocY = 0.0f; objLocZ = 0.0f;

  glfwGetFramebufferSize(window, &width, &height);
  aspect = (float)width / (float)height;
  pMat = glm::perspective(toRadians(45.f), aspect, 0.01f, 1000.f);
  
  setupVertices();
  shuttleTextureId = Utils::loadTexture("resources/spstob_1.jpg");

  /*lastLocX = (float)screen_width / 2.f;
  lastLocY = (float)screen_height / 2.f;*/
}


void window_size_callback(GLFWwindow* window, int newWidth, int newHeight)
{
  //螢幕坐标和視窗的幀緩沖
  /*GLFW在這裡和這裡解釋文檔中的兩個坐标系。
    簡而言之,視窗坐标是相對于螢幕和 / 或視窗的,并且以不一定對應于真實螢幕像素的人造單元給出。 當DPI縮放被激活時(例如,在帶有視網膜顯示器的Mac上),情況尤其如此。
    與視窗坐标相比,幀緩沖區的大小與像素相關,以便與glViewport OpenGLs要求相比對。
    請注意,在某些系統上,視窗坐标和像素坐标可以相同,但這不一定是正确的。*/
  aspect = (float)newWidth / (float)newHeight;
  glViewport(0, 0, newWidth, newHeight);

  cameraX = 0.0f; cameraY = 0.0f; cameraZ =4.f;
  objLocX = 0.0f; objLocY = 0.0f; objLocZ = 0.0f;
  mMat = glm::translate(glm::mat4(1.f), glm::vec3(objLocX, objLocY, objLocZ));
  //pMat = glm::perspective(glm::radians(45.f), aspect, 0.001f, 1000.f);
  pMat = glm::perspective(camera.Zoom, aspect, 0.001f, 1000.f);
}

void do_movement(void)
{
  if (keys[GLFW_KEY_W])
  {
    camera.ProcessKeyboard(FORWARD, deltaTime);
  }
  if (keys[GLFW_KEY_S])
  {
    camera.ProcessKeyboard(BACKWARD, deltaTime);
  }
  if (keys[GLFW_KEY_A])
  {
    camera.ProcessKeyboard(LEFT, deltaTime);
  }
  if (keys[GLFW_KEY_D])
  {
    camera.ProcessKeyboard(RIGHT, deltaTime);
  }
}

void press_key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
  if ((key == GLFW_KEY_ESCAPE) && (action == GLFW_PRESS))
  {
    glfwSetWindowShouldClose(window, GLFW_TRUE);
  }
  if (action == GLFW_PRESS)
  {
    keys[key] = GLFW_TRUE;
  }
  else if (action == GLFW_RELEASE)
  {
    keys[key] = GLFW_FALSE;
  }
}

void mouse_move_callback(GLFWwindow* window, double xPos, double yPos)
{
  if (firstMouse)
  {
    lastLocX = xPos;
    lastLocY = yPos;
    firstMouse = GL_FALSE;
  }

  double offsetLocX = xPos - lastLocX;
  double offsetLocY = lastLocY - yPos;

  lastLocX = xPos;
  lastLocY = yPos;

  camera.ProcessMouseMovement(offsetLocX, offsetLocY);
}

void mouse_scroll_callback(GLFWwindow* window, double xPos, double yPos)
{
  camera.ProcessMouseScroll(yPos);
}

void installLights(glm::mat4 vMatrix)
{
  transformed = glm::vec3(vMatrix * glm::vec4(currentLightPos, 1.f));
  lightPos[0] = transformed.x;
  lightPos[1] = transformed.y;
  lightPos[2] = transformed.z;

  // get the locations of the light and material fields in the shader
  globalAmbLoc = glGetUniformLocation(renderingProgram, "globalAmbient");
  ambLoc = glGetUniformLocation(renderingProgram, "light.ambient");
  diffLoc = glGetUniformLocation(renderingProgram, "light.diffuse");
  specLoc = glGetUniformLocation(renderingProgram, "light.specular");
  //specLoc = glGetUniformLocation(renderingProgram, "light.shininess");
  posLoc = glGetUniformLocation(renderingProgram, "light.position");
  mAmbLoc = glGetUniformLocation(renderingProgram, "material.ambient");
  mDiffLoc = glGetUniformLocation(renderingProgram, "material.diffuse");
  mSpecLoc = glGetUniformLocation(renderingProgram, "material.specular");
  mShiLoc = glGetUniformLocation(renderingProgram, "material.shininess");

  //  set the uniform light and material values in the shader
  glProgramUniform4fv(renderingProgram, globalAmbLoc, 1, globalAmbient);
  glProgramUniform4fv(renderingProgram, ambLoc, 1, lightAmbient);
  glProgramUniform4fv(renderingProgram, diffLoc, 1, lightDiffuse);
  glProgramUniform4fv(renderingProgram, specLoc, 1, lightSpecular);
  glProgramUniform3fv(renderingProgram, posLoc, 1, lightPos);
  glProgramUniform4fv(renderingProgram, mAmbLoc, 1, matAmbient);
  glProgramUniform4fv(renderingProgram, mDiffLoc, 1, matDiffuse);
  glProgramUniform4fv(renderingProgram, mSpecLoc, 1, matSpecular);
  glProgramUniform1f(renderingProgram, mShiLoc, matShininess);
}


void display(GLFWwindow* window, double currentTime)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glClearColor(0.1f, 0.5f, 0.5f, 1.f);

  //啟動着色器程式,在GPU上安裝GLSL代碼,這不會運作着色器程式,
  glUseProgram(renderingProgram);

  deltaTime = currentTime - lastFrame;
  lastFrame = currentTime;

  do_movement();

  //擷取uniform變量在着色器程式中的位置序号,通過該序号可以設定一緻變量的值,如果沒有該變量則傳回-1
  mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
  projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");
  nLoc = glGetUniformLocation(renderingProgram, "norm_matrix");

  mMat = glm::translate(glm::mat4(1.f), glm::vec3(objLocX, objLocY, objLocZ));
  //vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));
  vMat = camera.GetViewMatrix();


  mMat = glm::rotate(mMat, toRadians(0.f), glm::vec3(1.f, 0.f, 0.f));
  mMat = glm::rotate(mMat, toRadians(35.f), glm::vec3(0.f, 1.f, 0.f));
  mMat = glm::rotate(mMat, toRadians(35.f), glm::vec3(0.f, 0.f, 1.f));

  currentLightPos = glm::vec3(lightLoc.x, lightLoc.y, lightLoc.z);
  amt += 0.5f;
  rMat = glm::rotate(glm::mat4(1.f), glm::radians(amt), glm::vec3(0.f, 0.f, 1.f));
  currentLightPos = glm::vec3(rMat * glm::vec4(currentLightPos, 1.f));
  //這句必須要有,否則滑鼠中建失效
  pMat = glm::perspective(camera.Zoom, aspect, 0.01f, 1000.f);

  installLights(vMat);
  mvMat = vMat * mMat;
  invTrMat = glm::transpose(glm::inverse(mvMat));

  //更改一個uniform矩陣變量或數組的值。要更改的uniform變量的位置由location指定,location的值應該由glGetUniformLocation函數傳回
  // 将透視矩陣和MV 矩陣複制給相應的統一變量
  /*通過一緻變量(uniform修飾的變量)引用将一緻變量值傳入渲染管線。
    location : uniform的位置。
    count : 需要加載資料的數組元素的數量或者需要修改的矩陣的數量。
    transpose : 指明矩陣是列優先(column major)矩陣(GL_FALSE)還是行優先(row major)矩陣(GL_TRUE)。
    value : 指向由count個元素的數組的指針。
  */
  glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
  glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));  //GL_FALSE 參數不能錯否則無法顯示obj模型
  glUniformMatrix4fv(nLoc, 1, GL_FALSE, glm::value_ptr(invTrMat));

  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, shuttleTextureId);

  glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
  //指定了渲染時索引值為 index 的頂點屬性數組的資料格式和位置
  /*Parameters
  index
    指定要修改的頂點屬性的索引值

    size
    指定每個頂點屬性的元件數量。必須為1、2、3或者4。初始值為4。(夢維:如position是由3個(x, y, z)組成,而顔色是4個(r, g, b, a))

    type
    指定數組中每個元件的資料類型。可用的符号常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值為GL_FLOAT。

    normalized
    指定當被通路時,固定點資料值是否應該被歸一化(GL_TRUE)或者直接轉換為固定點值(GL_FALSE)。

    stride
    指定連續頂點屬性之間的偏移量。如果為0,那麼頂點屬性會被了解為:它們是緊密排列在一起的。初始值為0。

    pointer
    指定一個指針,指向數組中第一個頂點屬性的第一個元件。初始值為0。
    */
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
  //如果啟用,将通路通用頂點屬性數組中的值,并在調用頂點數組指令(如glDrawArrays或glDrawElements)時用于呈現。
  //對應頂點着色器的layout(location=0) in vec3 position;
  glEnableVertexAttribArray(0);

  glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
  glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
  glEnableVertexAttribArray(1);

  glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
  glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
  glEnableVertexAttribArray(2);

  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
  glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, 0);
  glEnableVertexAttribArray(3);

  glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, shuttleTextureId);

  glEnable(GL_DEPTH_TEST);
  //指定用于深度緩沖比較值;
  glDepthFunc(GL_LEQUAL);
  glDrawArrays(GL_TRIANGLES, 0, myModel.getNumVertices());
}




int main(int argc, char** argv)
{
  int glfwState = glfwInit();
  if (GLFW_FALSE == glfwState)
  {
    cout << "GLFW initialize failed,invoke glfwInit()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << endl;
    glfwTerminate();
    exit(EXIT_FAILURE);
  }

  /*因為我們要使用OpenGL 4.6,是以我們把GLFW_CONTEXT_VERSION_MAJOR和GLFW_CONTEXT_VERSION_MINOR對應的hint都設定為4和6。
  因為我們要使用OpenGL核心模式(這個後面會提到更多),是以我們把GLFW_OPENGL_PROFILE對應的hint設定為GLFW_OPENGL_CORE_PROFILE,
  表示使用OpenGL核心模式。最後,把GLFW_RESIZABLE對應的hint設定為GLFW_FALSE,表示視窗不允許使用者調整大小。
  之是以這樣做是因為如果允許使用者調整大小,大小發生變化後,視窗的繪制區域預設不變(依然是原來視窗的區域),
  也就是說視窗上繪制的圖像的大小、位置不會發生改變。為了避免這種現象發生,我們就簡單地不讓使用者調整視窗大小
  (當然也有更好的方法,就是用GLFW設定一個視窗大小的回調函數,但這樣比較簡單)。*/
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
  glfwWindowHint(GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_PROFILE);
  glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);

  GLFWwindow* window = glfwCreateWindow(screen_width, screen_height, "Load obj file model", nullptr, nullptr);
  if (!window)
  {
    cout << "GLFW create window failed,invoke glfwCreateWindow()......Error line:" << __FILE__ << "......Error line:" << __LINE__ << endl;
    glfwTerminate();
    exit(EXIT_FAILURE);
  }

  /*此函數使調用線程上的指定視窗的 OpenGL 或 OpenGL ES 上下文成為目前上下文。
    一次隻能在單個線程上使上下文成為目前上下文,并且每個線程一次隻能有一個目前上下文。
    線上程之間移動上下文時,必須先使其在舊線程上變為非目前狀态,然後再在新線程上變為目前狀态。
  */
  glfwMakeContextCurrent(window);
  glfwSetKeyCallback(window, press_key_callback);
  glfwSetCursorPosCallback(window, mouse_move_callback);
  glfwSetScrollCallback(window, mouse_scroll_callback);
  glfwSetWindowSizeCallback(window, window_size_callback);

  //設定滑鼠模式
  //glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);

  int glewState = glewInit();
  if (GLEW_OK != glewState)
  {
    cout << "GLEW initialize failed,invoke glewInit()......Error file:" << __FILE__ << "......Error line:" << __LINE__ << endl;
    glfwTerminate();
    exit(EXIT_FAILURE);
  }

  /*此函數設定目前 OpenGL 或 OpenGL ES 上下文的交換間隔,即從調用glfwSwapBuffers開始等待的螢幕更新次數,
    然後再交換緩沖區并傳回。這有時稱為垂直同步、垂直回掃同步或僅vsync。
    支援WGL_EXT_swap_control_tear和GLX_EXT_swap_control_tear擴充的上下文也接受負交換間隔,這允許驅動程式立即交換,
    即使幀到達有點晚。您可以使用glfwExtensionSupported檢查這些擴充。
    上下文必須在調用線程上是最新的。在沒有目前上下文的情況下調用此函數将導緻GLFW_NO_CURRENT_CONTEXT錯誤。
    此功能不适用于 Vulkan。如果您使用 Vulkan 進行渲染,請改為檢視交換鍊的目前模式。
  */
  glfwSwapInterval(1);

  printf("%s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));//開始初始化過程
  const GLubyte* renderer = glGetString(GL_RENDERER);
  const GLubyte* vendor = glGetString(GL_VENDOR);
  const GLubyte* version = glGetString(GL_VERSION);
  const GLubyte* glslVersion = glGetString(GL_SHADING_LANGUAGE_VERSION);
  GLint major, minor;
  glGetIntegerv(GL_MAJOR_VERSION, &major);
  glGetIntegerv(GL_MINOR_VERSION, &minor);
  printf("GL Vendor : %s\n", vendor);
  printf("GL Renderer : %s\n", renderer);
  printf("GL Version (string) : %s\n", version);
  printf("GL Version (integer) : %d.%d\n", major, minor);
  printf("GLSL Version : %s\n", glslVersion);

  glGetError(); // Debug GLEW bug fix


  /*因為我們要使用OpenGL 4.6,是以我們把GLFW_CONTEXT_VERSION_MAJOR和GLFW_CONTEXT_VERSION_MINOR對應的hint都設定為4和6。
  因為我們要使用OpenGL核心模式(這個後面會提到更多),是以我們把GLFW_OPENGL_PROFILE對應的hint設定為GLFW_OPENGL_CORE_PROFILE,
  表示使用OpenGL核心模式。最後,把GLFW_RESIZABLE對應的hint設定為GLFW_FALSE,表示視窗不允許使用者調整大小。
  之是以這樣做是因為如果允許使用者調整大小,大小發生變化後,視窗的繪制區域預設不變(依然是原來視窗的區域),
  也就是說視窗上繪制的圖像的大小、位置不會發生改變。為了避免這種現象發生,我們就簡單地不讓使用者調整視窗大小
  (當然也有更好的方法,就是用GLFW設定一個視窗大小的回調函數,但這樣比較簡單)。*/



  /*預設情況下,出于性能考慮,所有頂點着色器的屬性(Attribute)變量都是關閉的,
    意味着資料在着色器端是不可見的,哪怕資料已經上傳到GPU,由glEnableVertexAttribArray啟用指定屬性,
    才可在頂點着色器中通路逐頂點的屬性資料。glVertexAttribPointer或VBO隻是建立CPU和GPU之間的邏輯連接配接,
    進而實作了CPU資料上傳至GPU。但是,資料在GPU端是否可見,即,着色器能否讀取到資料,由是否啟用了對應的屬性決定,
    這就是glEnableVertexAttribArray的功能,允許頂點着色器讀取GPU(伺服器端)資料。
   */
  int nrAttributes;
  glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
  std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;

  init(window);

  while (!glfwWindowShouldClose(window))
  {
    display(window, (float)glfwGetTime());
    glfwSwapBuffers(window);
    glfwPollEvents();
  }

  glfwDestroyWindow(window);
  glfwTerminate();
  exit(EXIT_SUCCESS);

  return 0;
}      

2.頂點着色器

#version 460 core

layout(location = 0) in vec3 vertPos;
layout(location = 1) in vec2 texCoords;
layout(location = 2) in vec3 vertNormal;

out vec3 varyingNormal;
out vec3 varyingLightDir;
out vec3 varyingVertPos;
out vec3 varyingHalfVector;

struct PositionalLight
{
  vec4 ambient;
  vec4 diffuse;
  vec4 specular;
  vec3 position;
};

struct Material
{
  vec4 ambient;
  vec4 diffuse;
  vec4 specular;
  float shininess;
};

uniform vec4 globalAmbient;
uniform PositionalLight light;
uniform Material material;

uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform mat4 norm_matrix;

out vec2 tc;

layout(binding = 0) uniform sampler2D samp;

void main()
{
//  gl_Position = proj_matrix * mv_matrix * vec4(position, 1.f);
//  tc = texCoords;
  varyingVertPos = (mv_matrix * vec4(vertPos, 1.f)).xyz;
  varyingLightDir = light.position - varyingVertPos;
  varyingNormal = (norm_matrix * vec4(vertNormal, 1.f)).xyz;

  varyingHalfVector = normalize(normalize(varyingLightDir) + normalize(-varyingVertPos)).xyz;
  gl_Position = proj_matrix * mv_matrix * vec4(vertPos, 1.f);

  tc = texCoords;
}      

3.片元着色器

#version 460 core

in vec3 varyingNormal;
in vec3 varyingLightDir;
in vec3 varyingVertPos;
in vec3 varyingHalfVector;

out vec4 fragColor;

struct PositionalLight
{
  vec4 ambient;
  vec4 diffuse;
  vec4 specular;
  vec3 position;
};

struct Material
{
  vec4 ambient;
  vec4 diffuse;
  vec4 specular;
  float shininess;
};

uniform vec4 globalAmbient;
uniform PositionalLight light;
uniform Material material;

uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform mat4 norm_matrix;

in vec2 tc;
out vec4 textureColor;
uniform sampler2D samp;
out vec4 lightColor;

void main()
{
  // normalize the light, normal, and view vectors:
  vec3 L = normalize(varyingLightDir);
  vec3 N = normalize(varyingNormal);
  vec3 V = normalize(varyingVertPos);

  // get the angle between the light and surface normal:
  float cosTheta = dot(L, N);

  // halfway vector varyingHalfVector was computed in the vertex shader,
  // and interpolated prior to reaching the fragment shader.
  // It is copied into variable H here for convenience later.
  vec3 H = normalize(varyingHalfVector);

  // get angle between the normal and the halfway vector
  float cosPhi = dot(H, N);

  // compute ADS contributions (per pixel):
  vec3 ambient = ((globalAmbient * material.ambient) + (light.ambient * material.ambient)).xyz;
  vec3 diffuse = light.diffuse.xyz * material.diffuse.xyz * max(cosTheta, 0.f);
  vec3 specular = light.specular.xyz * material.specular.xyz * pow(max(cosPhi, 0.f), material.shininess * 3.f);
  
//  color = texture(samp, tc);
  textureColor = texture(samp, tc);
  lightColor = vec4((ambient + diffuse + specular), 1.f);
//  fragColor = 0.5 * textureColor + 0.5 * lightColor;

  fragColor = 0.25 * textureColor + 0.25 * lightColor ;
}      

運作效果

openGL結合光照與紋理

參考

源碼下載下傳