opengl 教程(18) 漫反射光
原帖地址:http://ogldev.atspace.co.uk/www/tutorial18/tutorial18.html
????? 環境光和漫反射光最大的區別在于:漫反射光依賴于光源的方向,而環境光和光源方向完全無關,環境光在場景中是均勻分布的,對場景中的所有物體都有效,而漫反射光在物體朝向光源的一面才有光照效果,在背面則沒有光照效果。
????? 如下圖所示,除了光源方向,漫反射光還和物體表面的法向有關。
????? 假設上圖中2個光束的強度都是一樣的,唯一不同的只是方向,則在左邊的圖中,物體表面光照要亮一些,因為光線是直射,而右邊的圖中,物體表面光照要弱一些,因為有一個角度。
????? 漫反射光照模型實際上基于Lambert's cosine law,也就是說物體表面反射的光照強度和與光源方向和物體表面法向有關,光源方向和法向的夾角的越小,則物體表面反射的光強越大。 用公式表示就是光源的強度乘以光源和物體表面法向夾角的余弦值。
????? 在上圖中,有四道光A,B,C,D,注意:物體表面法向和A重合,但方向相反。光線A和法向的夾角是0,余弦值就是1,所以A光速光照的強度最大,B次之,C和法向夾角是90度,所以光照強度是0,而D作用與物體的背面,余弦值是個負值,光照強度不可能是個負值,所以D的光照強度也是0,也就是說,光源要在物體表面有光照效果,光源方向和物體表面法向夾角必須是0-90度之間。
???? 如果物體表面是個平面,那么法向就是一個固定的向量,但真實物體表面往往并不是平面,所以物體表面的法向是不斷變化的,如下圖中的兩個法向。
???? 對三角形面來說,可以取它的面法向計算光照,此時三角形面上的光照是均勻的,但這種光照效果并不好,我們通常都是對每個頂點定義法向,而三角形面光柵化后的每個像素,它的法向是由頂點法向插值得到。這種光照模型稱作 Phong Shading, 下面就是頂點法向插值后的效果:
注意在片元shader中計算光照前,我們要把法向轉化到世界坐標系中去,這樣得到的光照效果才真實的光照模型。
主要代碼:
lighting_technique.h
struct DirectionalLight
{
??? Vector3f Color;
??? float AmbientIntensity;
??? Vector3f Direction;
??? float DiffuseIntensity;
};
新的方向光結構,增加了2個成員變量:一個是光源在世界坐標系中的方向,一個是漫反射的光照強度。
layout (location = 0) in vec3 Position;
layout (location = 1) in vec2 TexCoord;
layout (location = 2) in vec3 Normal;
uniform mat4 gWVP;
uniform mat4 gWorld;
out vec2 TexCoord0;
out vec3 Normal0;
void main()
{
??? gl_Position = gWVP * vec4(Position, 1.0);
??? TexCoord0 = TexCoord;
??? Normal0 = (gWorld * vec4(Normal, 0.0)).xyz;
}
????? 這是更新后的頂點shader代碼,我們增加了一個新的頂點屬性--法向,法向需要從物體的局部坐標系轉化到世界坐標系中,所以我們增加了一個uniform變量,用來傳入世界矩陣,該矩陣乘以法向向量,得到世界坐標系中的法向向量,最后把該向量傳入到片元shader中去。注意:該代碼中一些glsl語法,vec4表示四維向量,.xyz表示取向量的前三維。
in vec2 TexCoord0;
in vec3 Normal0;
out vec4 FragColor;
struct DirectionalLight
{
??? vec3 Color;
??? float AmbientIntensity;
??? float DiffuseIntensity;
??? vec3 Direction;
};
????? 片元shader中,首先會接受插值的紋理坐標和頂點法向,同時定義了一個法向光的結構,該結構和c++代碼中的結構是一致的。
void main()
{
??? vec4 AmbientColor = vec4(gDirectionalLight.Color, 1.0f) *
??????????? gDirectionalLight.AmbientIntensity;
?????? float DiffuseFactor = dot(normalize(Normal0), -gDirectionalLight.Direction);
????? 首先我們會計算環境光的強度,接著計算漫反射光的強度。我們使用光源方向向量的負值點乘像素的法向向量,得到光源和法向夾角的余弦值,最后用光源強度乘以余弦值得到最終的漫反射光強,如果余弦值是負的,那就說明光照在物體的背面,此時漫反射光強為0。
??? vec4 DiffuseColor;
??? if (DiffuseFactor > 0) {
??????? DiffuseColor = vec4(gDirectionalLight.Color, 1.0f) * gDirectionalLight.DiffuseIntensity * DiffuseFactor;
??? }
??? else {
??????? DiffuseColor = vec4(0, 0, 0, 0);
??? }
最后把紋理采樣的結果和光照的結果相乘,就是把它們混合起來,得到最終像素的輸出顏色。
??? FragColor = texture2D(gSampler, TexCoord0.xy) * (AmbientColor + DiffuseColor);
}
lighting_technique.cpp
void LightingTechnique::SetDirectionalLight(const DirectionalLight& Light)
{
??? glUniform3f(m_dirLightLocation.Color, Light.Color.x, Light.Color.y, Light.Color.z);
??? glUniform1f(m_dirLightLocation.AmbientIntensity, Light.AmbientIntensity);
??? Vector3f Direction = Light.Direction;
??? Direction.Normalize();
??? glUniform3f(m_dirLightLocation.Direction, Direction.x, Direction.y, Direction.z);
??? glUniform1f(m_dirLightLocation.DiffuseIntensity, Light.DiffuseIntensity);
}
上面這個函數設置傳入shader的方向光參數,注意方向光方向會被歸一化。另外也會增加一個設置世界矩陣的函數。
tutorial18.cpp
struct Vertex
{
??? Vector3f m_pos;
??? Vector2f m_tex;
??? Vector3f m_normal;
??? Vertex() {}
??? Vertex(Vector3f pos, Vector2f tex)
??? {
??????? m_pos = pos;
??????? m_tex = tex;
??????? m_normal = Vector3f(0.0f, 0.0f, 0.0f);
??? }
};
更新頂點結構,增加了法向,法向初始化為(0.0f, 0.0f, 0.0f)。
void CalcNormals(const unsigned int* pIndices, unsigned int IndexCount, Vertex* pVertices, unsigned int VertexCount)
{
??? for (unsigned int i = 0 ; i < IndexCount ; i += 3) {
??????? unsigned int Index0 = pIndices[i];
??????? unsigned int Index1 = pIndices[i + 1];
??????? unsigned int Index2 = pIndices[i + 2];
??????? Vector3f v1 = pVertices[Index1].m_pos - pVertices[Index0].m_pos;
??????? Vector3f v2 = pVertices[Index2].m_pos - pVertices[Index0].m_pos;
??????? Vector3f Normal = v1.Cross(v2);
??????? Normal.Normalize();
??????? pVertices[Index0].m_normal += Normal;
??????? pVertices[Index1].m_normal += Normal;
??????? pVertices[Index2].m_normal += Normal;
??? }
??? for (unsigned int i = 0 ; i < VertexCount ; i++) {
??????? pVertices[i].m_normal.Normalize();
??? }
}
上面這個函數計算三角形面的面法向,然后把三個頂點的法向都賦值面法向的值。
??? const Matrix4f& WorldTransformation = p.GetWorldTrans();
??? m_pEffect->SetWorldMatrix(WorldTransformation);
??? ...
??? glEnableVertexAttribArray(2);
??? ...
??? glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)20);
??? ...
??? glDisableVertexAttribArray(2);
pipeline類中增加了世界矩陣,它是縮放,旋轉,平移坐標的乘積。同時,我還使用了第三個頂點屬性,那就是法向。
程序執行后界面如下,可以通過x和z鍵控制漫反射強度。
總結
以上是生活随笔為你收集整理的opengl 教程(18) 漫反射光的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java I/O学习——File
- 下一篇: Animator 设置动画效果