一、颜色的表示
颜色纺锤体(颜色锥体)
颜色的视觉模型:颜色三特性的空间表示
明度(亮度):垂直轴线表示黑白亮度变化
色调:水平圆周上的不同角度点,代表了不同色调的颜色
饱和度:从圆心向圆周过渡表示,同一色调下饱和度的提高。某个平面圆形上的色调饱和度不同,而明度(亮度)相同。
CIE(Commission Internationale de L'Eclairage)色度图
CIE 1932色度图是用标称值表示的CIE色度图
其中,x表示红色分量,y表示绿色分量,E点代表白光,它的坐标是(0.33,0.33),边界上的数字表示光谱色的波长。
所欲单色光都位于舌形曲线上,这条曲线就是单色轨迹,曲线旁边标注的数字时单色(或称光谱色)光的波长值;而自然界中的各种实际颜色都位于这条闭合曲线内。
二、颜色模型
面向设备:RGB颜色模型、CMY颜色模型
面向用户:HSV颜色模型、HSL颜色模型
RGB颜色模型:
- 采用三维直角坐标系R-Red G-Green B-Blue
- 构成一个RGB颜色立方体
- 通常使用于彩色光栅图形显示设备中
- 真实感图形学中的主要颜色模型
CMY颜色模型
- 以红、绿、蓝的补色青(Cyan)、品红(Magenta)、黄(Yellow)为原色构成的颜色模型
- 常用于从白光中滤去某种颜色,又被成为减性原色系统,在白光中减去某种颜色来定义一种颜色
- 用于印刷行业中
HSV(HSB)颜色模型
HSV(HSB)颜色模型:一个基于颜色六边形的六棱锥
- H(Hue):色调,用角度度量,取值范围为0°~360° ,从红色开始按逆时针方向计算
- S(Saturation):饱和度,表示颜色接近光谱色的程度.一种颜色,可以看成是某种光谱色不白色混合的结果.通常取值范围为0%~100%,值越大,颜色越饱和.光谱色的白光成分为0,饱和度达到最高.
- V(Value或Brightness):明度,表示颜色明亮的程度.
HSL(HSI)颜色模型
- H(Hue):色调,使用不水平轴之间的角度来表示,范围从0 o到360o ,从蓝色开始
- S(Saturation):饱和度,说明颜色的相对浓度
- L(Lightness)或者I(Intensity):亮度,在L=0处为黑色,在L=1处为白色,灰度沿着L轴分布
采用近似的圆柱坐标系S=1,L=0.5 纯色彩S=0 仅有灰度
三、OpenGL颜色模型
两种颜色存储方式:
颜色值:像素点附加颜色信息之后,就必须为每一个像素点额外分配一个内存空间保存该点的颜色信息,对于RGB或者RGBA颜色模式,保存的数据直接代表其颜色值。
颜色索引:对于颜色索引模式,保存的是该颜色在颜色索引表中的位置,通过查颜色索引表对应到相应的颜色。颜色索引模式的优点是占用空间小,运行速度快,缺点是显示效果稍差。
随着硬件的提速升级,目前一般采用直接存储颜色值的方式。
RGB颜色模式
RGB模式中,RGB分别表示红绿蓝三色的分量,每个分量在0.0~1.0之间,通过设置RGB不同比例,可以获得任意颜色。
当我们想要获取一个橙色时,可以定义这样一个颜色向量
glm::vec3 coral(1.0f,0.5f,0.2f);
RGBA颜色模式:
RGB分别表示红绿蓝三色的分量,A(实际上是α系数,Alpha Coeefficient)表示颜色的透明度。
通过设置RGBA不同的值,可以获得任意的颜色。
在县市生活中,我们看到的物体的颜色其实是它所反射的颜色(不被物体吸收的颜色)。比如:当我们用白光照在一个蓝色的玩具上,这个蓝色的玩具会吸收白光中除了蓝色以外的所有子颜色,不被吸收的蓝色光被反射到我们眼中,让这个玩具看起来是蓝色的。
当我们把白色光源颜色与珊瑚色物体颜色值相乘,所得到的就是这个物体所反射的颜色。
LightIntensity*ObjectColor = Reflectcolor
(R,G,B)*(X,Y,Z)=(XR,YG,ZB);
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);
四、创建一个光照场景
我们使用之前创建的立方体箱子作为被投光对象,并用另一个立方体来代表光源。
顶点着色器
使用顶点着色器来绘制箱子。与之前的顶点着色器相比,容器的顶点位置是保持不变的,因此顶点着色器中没有新的代码。
#version 330 core
layout (location = 0)in vec3 apos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * model *vec4(apos,1.0);
}
创建灯立方体
由于后期的学习中会对顶点数据和属性指针频繁修改,所以我们为灯创建一个新的VAO。
unsigned int lightVAO;
glGenVertexArray(1,&lightVAO);
glBindVertexArray(lightVAO);
//只需要绑定VBO不用再次设置VBO的数据,因为箱子的VBO数据中已经包含了正确的立方体顶点数据
glBindBuffer(GL_ARRAY_BUFFER,VBO);
//设置灯立方体的顶点属性(对我们来说仅仅只有位置数据)
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3*sizeof(float),(void*)0);
glEnableVertexAttribArray(0);
片段着色器
这个片段着色器从uniform变量中接受物体的颜色和光源的颜色。
#version 330 core
out vec4 FragColor;
uniform vec3 objectColor;
uniform vec3 lightColor;
void main()
{
FragColor = vec4(lightColor * objectColor,1.0);
}
lightingShader.use();
lightingShader.setVec3("objectColor",1.0f,0.5f,0.31f);
lightingShader.setVec3("lightColor",1.0f,1.0f,1.0f);
当修改顶点或者片段着色器后,灯的位置和颜色也会随之改变,我们并不希望这样。所以我们要为灯的绘制创建另外一套着色器,从而能够保证它能在其它光照着色器发生改变的时候不受影响。
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0);//将向量的四个分量全部设置为1.0
}
当我们想要绘制我们的物体时,我们需要使用刚刚定义的着色器来绘制箱子。当我们想要绘制灯的时候,我们会使用灯的着色器。
为了显示真正的灯,我们将表示光源的立方体与光源绘制在相同的位置。
声明一个全局vec3变量来表示光源在场景的世界空间坐标中的位置。
glm::vec3 lightPos(1.2f,1.0f,2.0f);
把灯移动到这个位置,并将其缩小。
model = glm::mat4();
model = glm::translate(model,lightPos);
model = glm::scale(model,glm::vec3(0.2f);
绘制灯立方体
lamShader.use();
//设置模型、视图、和投影矩阵uniform
//绘制灯立方体对象
glBindVertexArrar(lightVAO);
glDrawArrays(GL_TRIANGLES,0,36);
在这里分享一个我新学到的技巧:
在一大段代码的开头加#pragma region *** ,结尾加#pragma endregion,然后在开头的#pragma region ***前面按一下tab,就可以把它们都收起来。
这里有对之前的代码进行整理
fragmentSource.txt
#version 330 core
in vec2 TexCoord;
in vec4 vertexColor;
// texture samplers
uniform sampler2D ourTexture;
uniform sampler2D ourFace;
uniform vec3 objColor;
uniform vec3 ambientColor;
out vec4 FragColor;
void main()
{
// linearly interpolate between both textures (80% container, 20% awesomeface)
//FragColor = mix(texture(ourTexture, TexCoord), texture(ourFace, TexCoord), 0.2);
FragColor = vec4(objColor * ambientColor,1.0)*texture(ourTexture, TexCoord)* texture(ourFace, TexCoord);
}
vertexSource.txt
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout(location = 2) in vec2 aTexCoord;
uniform mat4 modelMat;
uniform mat4 viewMat;
uniform mat4 projMat;
out vec2 TexCoord;
out vec4 vertexColor;
void main()
{
gl_Position = projMat * viewMat * modelMat * vec4(aPos, 1.0);
vertexColor = vec4(aColor.x,aColor.y,aColor.z,1);
TexCoord = aTexCoord;
}
main.cpp
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include"Shader.h"
#include"Camera.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT =600;
#pragma region Camera Declare
Camera camera(glm::vec3(0, 0, 3.0f), glm::radians(-15.0f), glm::radians(180.0f), glm::vec3(0, 1.0f, 0));
#pragma endregion
#pragma region Input Declare
float lastX;
float lastY;
bool firstMouse = true;
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, true);
}
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
{
camera.speedZ = 1.0f;
}
else if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
{
camera.speedZ = -1.0f;
}
else
{
camera.speedZ = 0;
}
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse == true)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float deltaX, deltaY;
deltaX = xpos - lastX;
deltaY = ypos - lastY;
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(deltaX, deltaY);
}
#pragma endregion
unsigned int LoadImageToGPU(const char*filename, GLint internalformat, GLenum format, int textureSlot)
{
unsigned int texBuffer;
glGenTextures(1, &texBuffer);
glActiveTexture(GL_TEXTURE0 + textureSlot);
glBindTexture(GL_TEXTURE_2D, texBuffer);
int width, height, nrChannels;
stbi_set_flip_vertically_on_load(true); // tell stb_image.h to flip loaded texture's on the y-axis.
unsigned char *data = stbi_load(filename, &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
return texBuffer;
}
int main()
{
#pragma region Open a window
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// configure global opengl state
// -----------------------------
glEnable(GL_DEPTH_TEST);
#pragma endregion
#pragma region Model Data
float vertices[] = {
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
};
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)
};
#pragma endregion
#pragma region Init Shader Pragram
Shader ourShader("VertexSource.txt", "fragmentSource.txt");
#pragma endregion
#pragma region Init and Load Models to VAO,VBO
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// texture coord attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
#pragma endregion
#pragma region Init and Load Texture
unsigned int texBufferA;
texBufferA = LoadImageToGPU("container.jpg",GL_RGB,GL_RGB,0);
unsigned int texBufferB;
texBufferB = LoadImageToGPU("awesomeface.png",GL_RGBA, GL_RGBA, 1);
#pragma endregion
// tell opengl for each sampler to which texture unit it belongs to (only has to be done once)
// -------------------------------------------------------------------------------------------
#pragma region Prepare MVP matrices
glm::mat4 modelMat;
glm::mat4 viewMat;
glm::mat4 projMat;
projMat = glm::perspective(glm::radians(45.0f), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
#pragma endregion
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
processInput(window);
// clear srceen
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // also clear the depth buffer now!
viewMat = camera.GetViewMatrix();
for (unsigned int i = 0; i < 10; i++)
{
//set Model Matrix
modelMat = glm::translate(glm::mat4(1.0f), cubePositions[i]);
//set View and Project Matrices here
//set Material->shader program
ourShader.use();
//set Material->textures
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texBufferA);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texBufferB);
//set material->uniforms
glUniform1i(glGetUniformLocation(ourShader.ID, "ourTexture"), 0);
glUniform1i(glGetUniformLocation(ourShader.ID, "ourFace"), 1);
unsigned int modelLoc = glGetUniformLocation(ourShader.ID, "modelMat");
unsigned int viewLoc = glGetUniformLocation(ourShader.ID, "viewMat");
unsigned int projectLoc = glGetUniformLocation(ourShader.ID, "projMat");
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(modelMat));
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(viewMat));
glUniformMatrix4fv(projectLoc, 1, GL_FALSE, glm::value_ptr(projMat));
glUniform3f(glGetUniformLocation(ourShader.ID, "objColor"), 1.0f, 0.5f, 0.31f);
glUniform3f(glGetUniformLocation(ourShader.ID, "ambientColor"), 1.0f, 1.0f, 1.0f);
// set Model
glBindVertexArray(VAO);
//Drawcall
glDrawArrays(GL_TRIANGLES, 0, 36);
}
//Clean up,prepare for next render loop
glfwSwapBuffers(window);
glfwPollEvents();
camera.UpdataCameraPos();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
其他的camera类和Shader没有发生改变。