計算機圖形學作業( 五):畫一個立方體並實現 Phong Shading 和 Gouraud Shading 兩種陰影

顏色和光照場景

我們在現實生活中看到某一物體的顏色並不是這個物體真正擁有的顏色,而是它所反射的顏色。換句話說,那些不能被物體所吸收的顏色(被拒絕的顏色)就是我們能夠感知到的物體的顏色。例如,太陽光能被看見的白光其實是由許多不同的顏色組合而成的(如下圖所示)。如果我們將白光照在一個藍色的玩具上,這個藍色的玩具會吸收白光中除了藍色以外的所有子顏色,不被吸收的藍色光被反射到我們的眼中,讓這個玩具看起來是藍色的。下圖顯示的是一個珊瑚紅的玩具,它以不同強度反射了多個顏色。

所以,在 OpenGL 中,我們要創建一個自然光源和一個物體,那麼要計算物體的反射顏色,就將自然光源和物體這兩個顏色的向量作分量相乘,如下:

glm::vec3 lightColor(1.0f, 1.0f, 1.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f);

如果自然光源不是白光,而是其它顏色光,那麼物體反射出來的顏色也會作相應的改變。

爲了模擬 OpenGL 中的顏色、陰影,我們要創建一個光照場景,包括一個自然光源和一個正方體,爲我們後面的工作作準備。爲了不產生混淆,自然光源和物體使用不同的VAO。結果如下:

Phong Shading

在 OpenGL 中,常用的一個光照模型被稱爲馮氏光照模型(Phong Lighting Model)。馮氏光照模型的主要結構由3個分量組成:環境(Ambient)、漫反射(Diffuse)和鏡面(Specular)光照。下面這張圖展示了這些光照分量看起來的樣子:

  • 環境光照(Ambient Lighting):即使在黑暗的情況下,世界上通常也仍然有一些光亮(月亮、遠處的光),所以物體幾乎永遠不會是完全黑暗的。爲了模擬這個,我們會使用一個環境光照常量,它永遠會給物體一些顏色。
  • 漫反射光照(Diffuse Lighting):模擬光源對物體的方向性影響(Directional Impact)。它是馮氏光照模型中視覺上最顯著的分量。物體的某一部分越是正對着光源,它就會越亮。
  • 鏡面光照(Specular Lighting):模擬有光澤物體上面出現的亮點。鏡面光照的顏色相比於物體的顏色會更傾向於光的顏色。

環境光照

我們使用一個很小的常量(光照)顏色,添加到物體片段的最終顏色中,這樣子的話即便場景中沒有直接的光源也能看起來存在有一些發散的光。

把環境光照添加到場景裏非常簡單:我們用光的顏色乘以一個很小的常量環境因子,再乘以物體的顏色,然後將最終結果作爲片段的顏色,片段着色器代碼如下:

void main()
{
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;

    vec3 result = ambient * objectColor;
    FragColor = vec4(result, 1.0);
}

漫反射光照

漫反射光照使物體上與光線方向越接近的片段能從光源處獲得更多的亮度。爲了實現這一點,我們利用自然光源發出的光線的向量與物體表面法向量形成的夾角,計算物體表面的漫反射光照。

法向量是一個垂直於頂點表面的向量,我們通過頂點着色器直接傳入立方體六個面對應的法向量的值,頂點數據可以在官方資源中找到。要注意的是,當物體進行了放縮、旋轉等操作時,我們在頂點着色器傳入的法向量值就變得不準確了,所以我們要在頂點着色器對法向量進行處理,以保證準確性,如下:

Normal = mat3(transpose(inverse(model))) * aNormal;

最後,在片段着色器重,就可以進行漫反射光照的計算了:

//漫反射
 vec3 norm = normalize(Normal);
 vec3 lightDir = normalize(lightPos - FragPos);
 float diff = max(dot(norm, lightDir), 0.0);
 vec3 diffuse = diffuseStrength * diff * lightColor;
 

鏡面光照

鏡面光照也是依據光的方向向量和物體的法向量來決定的,但是它也依賴於觀察方向,例如玩家是從什麼方向看着這個片段的。鏡面光照是基於光的反射特性。如果我們想象物體表面像一面鏡子一樣,那麼,無論我們從哪裏去看那個表面所反射的光,鏡面光照都會達到最大化。

所以比起漫反射光照,我們需要多一個觀察者的位置變量,然後便可進行鏡面光照計算,如下:

//鏡面光
 vec3 viewDir = normalize(viewPos - FragPos);
 vec3 reflectDir = reflect(-lightDir, norm);  
 float spec = pow(max(dot(viewDir, reflectDir), 0.0), ShininessStrength);
 vec3 specular = specularStrength * spec * lightColor;  
 

着色器代碼

頂點着色器:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out vec3 FragPos;
out vec3 Normal;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = mat3(transpose(inverse(model))) * aNormal;  
    
    gl_Position = projection * view * vec4(FragPos, 1.0);
}

片段着色器:

#version 330 core

out vec4 FragColor;

in vec3 Normal;  
in vec3 FragPos;  

uniform float ambientStrength;
uniform float diffuseStrength;
uniform float specularStrength; 
uniform int ShininessStrength;

uniform vec3 lightPos; 
uniform vec3 objectColor;
uniform vec3 lightColor;
uniform vec3 viewPos; 

void main() {
	//環境光
    vec3 ambient = ambientStrength * lightColor;

	//漫反射
	vec3 norm = normalize(Normal);
	vec3 lightDir = normalize(lightPos - FragPos);
	float diff = max(dot(norm, lightDir), 0.0);
	vec3 diffuse = diffuseStrength * diff * lightColor;

	//鏡面光
	vec3 viewDir = normalize(viewPos - FragPos);
	vec3 reflectDir = reflect(-lightDir, norm);  
	float spec = pow(max(dot(viewDir, reflectDir), 0.0), ShininessStrength);
	vec3 specular = specularStrength * spec * lightColor;  

	vec3 result = (ambient + diffuse + specular) * objectColor;
    FragColor = vec4(result, 1.0);
}

結果

最終,基於Phong Shading 模型的效果圖如下:

Gouraud Shading

Phong Shading 是在片段着色器實現馮氏光照模型,而Gouraud Shading是在頂點着色器實現的馮氏光照模型。所以要實現 Gouraud Shading,只需要修改兩個着色器的代碼,把片段着色器中關於馮氏光照模型的計算移到頂點着色器即可。

在頂點着色器中做光照的優勢是,相比片段來說,頂點要少得多,因此會更高效,所以(開銷大的)光照計算頻率會更低。然而,頂點着色器中的最終顏色值是僅僅只是那個頂點的顏色值,片段的顏色值是由插值光照顏色所得來的。結果就是這種光照看起來不會非常真實,除非使用了大量頂點。

着色器代碼

頂點着色器:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;

out vec3 LightingColor; 

uniform float ambientStrength;
uniform float diffuseStrength;
uniform float specularStrength; 
uniform int ShininessStrength;

uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    
    // gouraud shading
    vec3 Position = vec3(model * vec4(aPos, 1.0));
    vec3 Normal = mat3(transpose(inverse(model))) * aNormal;
    
    // 環境光
    vec3 ambient = ambientStrength * lightColor;
  	
    // 漫反射 
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - Position);
    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diffuseStrength * diff * lightColor;
    
    // 鏡面光
    vec3 viewDir = normalize(viewPos - Position);
    vec3 reflectDir = reflect(-lightDir, norm);  
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), ShininessStrength);
    vec3 specular = specularStrength * spec * lightColor;      

    LightingColor = ambient + diffuse + specular;
}

片段着色器:

#version 330 core
out vec4 FragColor;

in vec3 LightingColor; 

uniform vec3 objectColor;

void main()
{
   FragColor = vec4(LightingColor * objectColor, 1.0);
}

結果

最終基於 Gouraud Shading 模型的效果圖如下:

光源來回移動(加分項)

要實現光源的來回移動並不難,我先定義了光源的座標,如下:

glm::vec3 lightPos(20.0f, 8.0f, 20.0f);

然後在繪圖的循環中不斷控制 light.x 的增減,即可實現觀察者左右來回移動,如下:

//燈光左右移動
if (enable_translate) {
	if (is_translate_to_left) {
		lightPos.x = lightPos.x - 0.5f;
		if (lightPos.x < -30) {
			is_translate_to_left = false;
		}
	}
	else {
		lightPos.x = lightPos.x + 0.5f;
		if (lightPos.x > 30) {
			is_translate_to_left = true;
		}
	}	
}
		

代碼

寫代碼時,爲了方便地使用shader,我使用了官方的shader類庫,引入該文件後,便可以方便地創建和使用shader。

#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <glm.hpp>
#include <gtc/matrix_transform.hpp>
#include <gtc/type_ptr.hpp>

//使用了官方的shader庫文件,可以更方便地操作shader
//注意,使用官方庫操作shadder時,自己寫的頂點和片段着色器代碼必須另外寫成文件
//然後把文件路徑傳入shader.h提供的構造函數
#include "shader.h"  
#include "camera.h"

using namespace std;

const int WINDOW_WIDTH = 1000;
const int WINDOW_HEIGHT = 800;

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow* window);

//camera的位置與後面物體的view向量相反
Camera camera(glm::vec3(0.0f, 0.0f, 10.0f));
//鼠標位置
float mouseX = 0.0f;
float mouseY = 0.0f;
bool firstMouse = true;
//每一幀間隔時間
float deltaTime = 0.0f;
//上一幀的時刻
float lastFrame = 0.0f;

int main() {
	//初始化opengl窗口和配置
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	
	GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "LearnOpenGL", NULL, NULL);
	if (window == NULL) {
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	//深度測試
	glEnable(GL_DEPTH_TEST);

	//創建並綁定ImGui
	const char* glsl_version = "#version 130";
	IMGUI_CHECKVERSION();
	ImGui::CreateContext();
	ImGuiIO& io = ImGui::GetIO(); (void)io;
	ImGui::StyleColorsDark();
	ImGui_ImplGlfw_InitForOpenGL(window, true);
	ImGui_ImplOpenGL3_Init(glsl_version);

	//正方體邊長爲2.0f*2s
	float half_edge = 2.0f;
	glm::vec3 verticalVec1 = glm::vec3(0.0f, 0.0f, -1.0f);
	glm::vec3 verticalVec2 = glm::vec3(0.0f, 0.0f, 1.0f);
	glm::vec3 verticalVec3 = glm::vec3(-1.0f, 0.0f, 0.0f);
	glm::vec3 verticalVec4 = glm::vec3(1.0f, 0.0f, 0.0f);
	glm::vec3 verticalVec5 = glm::vec3(0.0f, -1.0f, 0.0f);
	glm::vec3 verticalVec6 = glm::vec3(0.0f, 1.0f, 0.0f);

	float vertices[] = {
	-half_edge, -half_edge, -half_edge, verticalVec1.x, verticalVec1.y, verticalVec1.z,
	 half_edge, -half_edge, -half_edge, verticalVec1.x, verticalVec1.y, verticalVec1.z,
	 half_edge,  half_edge, -half_edge, verticalVec1.x, verticalVec1.y, verticalVec1.z,
	 half_edge,  half_edge, -half_edge, verticalVec1.x, verticalVec1.y, verticalVec1.z,
	-half_edge,  half_edge, -half_edge, verticalVec1.x, verticalVec1.y, verticalVec1.z,
	-half_edge, -half_edge, -half_edge, verticalVec1.x, verticalVec1.y, verticalVec1.z,

	-half_edge, -half_edge,  half_edge, verticalVec2.x, verticalVec2.y, verticalVec2.z,
	 half_edge, -half_edge,  half_edge, verticalVec2.x, verticalVec2.y, verticalVec2.z,
	 half_edge,  half_edge,  half_edge, verticalVec2.x, verticalVec2.y, verticalVec2.z,
	 half_edge,  half_edge,  half_edge, verticalVec2.x, verticalVec2.y, verticalVec2.z,
	-half_edge,  half_edge,  half_edge, verticalVec2.x, verticalVec2.y, verticalVec2.z,
	-half_edge, -half_edge,  half_edge, verticalVec2.x, verticalVec2.y, verticalVec2.z,

	-half_edge,  half_edge,  half_edge, verticalVec3.x, verticalVec3.y, verticalVec3.z,
	-half_edge,  half_edge, -half_edge, verticalVec3.x, verticalVec3.y, verticalVec3.z,
	-half_edge, -half_edge, -half_edge, verticalVec3.x, verticalVec3.y, verticalVec3.z,
	-half_edge, -half_edge, -half_edge, verticalVec3.x, verticalVec3.y, verticalVec3.z,
	-half_edge, -half_edge,  half_edge, verticalVec3.x, verticalVec3.y, verticalVec3.z,
	-half_edge,  half_edge,  half_edge, verticalVec3.x, verticalVec3.y, verticalVec3.z,

	 half_edge,  half_edge,  half_edge, verticalVec4.x, verticalVec4.y, verticalVec4.z,
	 half_edge,  half_edge, -half_edge, verticalVec4.x, verticalVec4.y, verticalVec4.z,
	 half_edge, -half_edge, -half_edge, verticalVec4.x, verticalVec4.y, verticalVec4.z,
	 half_edge, -half_edge, -half_edge, verticalVec4.x, verticalVec4.y, verticalVec4.z,
	 half_edge, -half_edge,  half_edge, verticalVec4.x, verticalVec4.y, verticalVec4.z,
	 half_edge,  half_edge,  half_edge, verticalVec4.x, verticalVec4.y, verticalVec4.z,

	-half_edge, -half_edge, -half_edge, verticalVec5.x, verticalVec5.y, verticalVec5.z,
	 half_edge, -half_edge, -half_edge, verticalVec5.x, verticalVec5.y, verticalVec5.z,
	 half_edge, -half_edge,  half_edge, verticalVec5.x, verticalVec5.y, verticalVec5.z,
	 half_edge, -half_edge,  half_edge, verticalVec5.x, verticalVec5.y, verticalVec5.z,
	-half_edge, -half_edge,  half_edge, verticalVec5.x, verticalVec5.y, verticalVec5.z,
	-half_edge, -half_edge, -half_edge, verticalVec5.x, verticalVec5.y, verticalVec5.z,

	-half_edge,  half_edge, -half_edge, verticalVec6.x, verticalVec6.y, verticalVec6.z,
	 half_edge,  half_edge, -half_edge, verticalVec6.x, verticalVec6.y, verticalVec6.z,
	 half_edge,  half_edge,  half_edge, verticalVec6.x, verticalVec6.y, verticalVec6.z,
	 half_edge,  half_edge,  half_edge, verticalVec6.x, verticalVec6.y, verticalVec6.z,
	-half_edge,  half_edge,  half_edge, verticalVec6.x, verticalVec6.y, verticalVec6.z,
	-half_edge,  half_edge, -half_edge, verticalVec6.x, verticalVec6.y, verticalVec6.z,
	};

	unsigned int indices[] = { // 注意索引從0開始! 
		0, 1, 2, // 第一個三角形
		3, 4, 5,

		6, 7, 8,
		9, 10, 11,
		
		12, 13, 14,
		15, 16, 17,

		18, 19, 20,
		21, 22, 23,

		24, 25, 26,
		27, 28, 29,

		30, 31, 32,
		33, 34, 35
	};

	//物體(正方體)的VAO
	unsigned int objectVAO;
	unsigned int VBO;
	unsigned int EBO;

	//必須先綁定VA0
	glGenVertexArrays(1, &objectVAO);
	glBindVertexArray(objectVAO);

	//再綁定VBO
	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	//使用EBO畫多個三角形,組合成其它圖形
	glGenBuffers(1, &EBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
	
	//再設置屬性
	//位置屬性
	//屬性位置值爲0的頂點屬性,有3個值,頂點着色器總參數大小爲6個float,位置屬性偏移爲0
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);

	//法向量屬性
	//屬性位置值爲0的頂點屬性,有3個值,頂點着色器總參數大小爲6個float,位置屬性偏移爲3個float
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));
	glEnableVertexAttribArray(1);

	//燈光VAO
	unsigned int lightVAO;
	glGenVertexArrays(1, &lightVAO);
	glBindVertexArray(lightVAO);

	glBindBuffer(GL_ARRAY_BUFFER, VBO);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

	//頂點着色器總參數大小爲6個float,是爲了與前面的objectVAO保持一致
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);

	float scaleDelta = 1.0f;
	float rotateAngle = glm::radians(15.0f);
	float perspective_fov = 45.0f;
	float perspective_division = (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT;
	float perspective_near = 0.1f;
	float perspective_far = 100.0f;
	bool enable_camera = false;
	bool is_phong_shader = true;
	bool is_gouraud_shader = false;
	bool enable_translate = false;
	bool is_translate_to_left = true;
	bool show_window = true;
	float ambientStrength = 0.1f;
	float diffuseStrength = 1.0f;
	float specularStrength = 1.0f;
	int ShininessStrength = 32;
	glm::vec3 lightPos(20.0f, 8.0f, 20.0f);
	glm::vec3 viewPos;

	while (!glfwWindowShouldClose(window)) {
		if (enable_camera) {
			//告訴glfw獲取我們的鼠標
			glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
		}
		else {
			glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
		}
	
		//計算當前時間和幀間隔時間,爲了使移動更平滑
		float currentFrame = glfwGetTime();
		deltaTime = currentFrame - lastFrame;
		lastFrame = currentFrame;

		processInput(window);

		//清除屏幕
		glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		//創建座標變換
		glm::mat4 model = glm::mat4(1.0f);
		glm::mat4 view = glm::mat4(1.0f);
		glm::mat4 projection = glm::mat4(1.0f);
		
		if (enable_camera) {
			view = camera.GetViewMatrix();
			viewPos = camera.Position;
			
			//它的第一個參數定義了fov的值,它表示的是視野,並且設置了觀察空間的大小。第二個參數設置了寬高比。第三和第四個參數設置了平截頭體的近和遠平面
			//glm::radians()是把角度轉爲弧度
			//利用鼠標放縮
			projection = glm::perspective(glm::radians(camera.Zoom), perspective_division, perspective_near, perspective_far);

		}
		else {
			//把場景移動(-1.0f, -1.0f, -5.0f)
			view = glm::translate(view, glm::vec3(0.0f, 0.0f, -10.0f));
			viewPos = glm::vec3(1.0f, 1.0f, 5.0f);
			
			projection = glm::perspective(glm::radians(perspective_fov), perspective_division, perspective_near, perspective_far);
		}

		//燈光左右移動
		if (enable_translate) {
			if (is_translate_to_left) {
				lightPos.x = lightPos.x - 0.5f;
				if (lightPos.x < -30) {
					is_translate_to_left = false;
				}
			}
			else {
				lightPos.x = lightPos.x + 0.5f;
				if (lightPos.x > 30) {
					is_translate_to_left = true;
				}
			}	
		}

		//利用官方庫shader.h構造着色器
		if (is_phong_shader) {
			Shader objectShader("objectVertexPhongShader.vs", "objectFragmentPhongShader.fs");
			//使用着色器程序
			objectShader.use();
			//設置着色器程序uniform
			objectShader.setFloat("ambientStrength", ambientStrength);
			objectShader.setFloat("diffuseStrength", diffuseStrength);
			objectShader.setFloat("specularStrength", specularStrength);
			objectShader.setInt("ShininessStrength", ShininessStrength);

			objectShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
			objectShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
			objectShader.setVec3("lightPos", lightPos);
			objectShader.setVec3("viewPos", viewPos);

			objectShader.setMat4("view", view);
			objectShader.setMat4("projection", projection);
			//model包含了位移、縮放與旋轉操作
			model = glm::mat4(1.0f);
			model = glm::rotate(model, rotateAngle, glm::vec3(0.0f, 1.0f, 0.0f));
			objectShader.setMat4("model", model);

			//畫物體
			glBindVertexArray(objectVAO);
			glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
		}
		else if (is_gouraud_shader) {
			Shader objectShader("objectVertexGouraudShader.vs", "objectFragmentGouraudShader.fs");
			//使用着色器程序
			objectShader.use();
			//設置着色器程序uniform
			objectShader.setFloat("ambientStrength", ambientStrength);
			objectShader.setFloat("diffuseStrength", diffuseStrength);
			objectShader.setFloat("specularStrength", specularStrength);
			objectShader.setInt("ShininessStrength", ShininessStrength);

			objectShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
			objectShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
			objectShader.setVec3("lightPos", lightPos);
			objectShader.setVec3("viewPos", viewPos);

			objectShader.setMat4("view", view);
			objectShader.setMat4("projection", projection);
			//model包含了位移、縮放與旋轉操作
			model = glm::mat4(1.0f);
			model = glm::rotate(model, rotateAngle, glm::vec3(0.0f, 1.0f, 0.0f));
			objectShader.setMat4("model", model);

			//畫物體
			glBindVertexArray(objectVAO);
			glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
		}

		Shader lightShader("lightVertexShader.vs", "lightFragmentShader.fs");

		lightShader.use();
		lightShader.setMat4("view", view);
		lightShader.setMat4("projection", projection);
		model = glm::mat4(1.0f);
		model = glm::scale(model, glm::vec3(0.2f));
		model = glm::translate(model, lightPos);
		lightShader.setMat4("model", model);

		//畫燈光
		glBindVertexArray(lightVAO);
		glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

		//創建imgui
		ImGui_ImplOpenGL3_NewFrame();
		ImGui_ImplGlfw_NewFrame();
		ImGui::NewFrame();

		ImGui::Begin("Edit cube", &show_window, ImGuiWindowFlags_MenuBar);

		if (ImGui::Button("enable camera")) {
			if (enable_camera) {
				enable_camera = false;
			}
			else {
				enable_camera = true;
			}
		}
		
		if (ImGui::RadioButton("Phong Shader", is_phong_shader)) {
			is_gouraud_shader = false;
			is_phong_shader = true;
		}
		if (ImGui::RadioButton("Gouraud Shader", is_gouraud_shader)) {
			is_gouraud_shader = true;
			is_phong_shader = false;
		}
		ImGui::SliderFloat("ambientStrength", &ambientStrength, 0.01f, 3.0f);
		ImGui::SliderFloat("diffuseStrength", &diffuseStrength, 0.01f, 3.0f);
		ImGui::SliderFloat("specularStrength", &specularStrength, 0.01f, 5.0f);
		ImGui::SliderInt("ShininessStrength", &ShininessStrength, 0, 256);
		
		if (ImGui::Button("enable translate")) {
			if (enable_translate) {
				enable_translate = false;
			}
			else {
				enable_translate = true;
			}
		}

		ImGui::End();

		ImGui::Render();
		int display_w, display_h;
		glfwMakeContextCurrent(window);
		glfwGetFramebufferSize(window, &display_w, &display_h);
		glViewport(0, 0, display_w, display_h);
		ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

		glfwPollEvents();
		glfwSwapBuffers(window);
	}

	glDeleteVertexArrays(1, &objectVAO);
	glDeleteVertexArrays(1, &lightVAO);
	glDeleteBuffers(1, &VBO);
	glDeleteBuffers(1, &EBO);

	glfwTerminate();

	return 0;
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
	glViewport(0, 0, width, height);
}

void processInput(GLFWwindow* window) {
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
		glfwSetWindowShouldClose(window, true);
	}

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
		camera.ProcessKeyboard(FORWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
		camera.ProcessKeyboard(BACKWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
		camera.ProcessKeyboard(LEFT, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
		camera.ProcessKeyboard(RIGHT, deltaTime);
}

//鼠標移動
void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
	if (firstMouse)
	{
		mouseX = xpos;
		mouseY = ypos;
		firstMouse = false;
	}

	float xoffset = xpos - mouseX;
	float yoffset = mouseY - ypos;

	mouseX = xpos;
	mouseY = ypos;

	camera.ProcessMouseMovement(xoffset, yoffset);
}

//鼠標放縮
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
	camera.ProcessMouseScroll(yoffset);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章