opengl 教程(19) 高光

原帖地址 http://ogldev.atspace.co.uk/www/tutorial19/tutorial19.html

      最初我们计算环境光的时候,唯一影响光照的就是光的强度,接着在漫反射光计算时,我们引入了光源的方向以及物体顶点法线的概念,在本篇教程中,我们学习如何计算高光,我们会再次引入一个新的参数视点位置,因为高光会随着视点的移动而改变位置。在一些角度,高光看起来会更亮。金属物体通常都有高光的效果。

      下面的高光计算中会引入视点位置,首先看一下下面的光照图:

这儿有5个参数需要我们注意:

  • 'I' 是照亮物体表面的入射光
  • 'N' 是物体表面法向
  • 'R' 是入射光在照射到物体表面后的发射光,反射光和入射光是沿着法线对称的。
  • 'V' 是物体表面上的点指向视点的向量。
  • 'α' 'R' 和 'V'两个向量的夹角

       角度'α'为0的时候,R和V重合,此时高光的强度最大,当观察者视线逐渐离开的时候,α逐渐变大,高光效果会逐渐变小。基于这个依据,我们将用点积操作来求得α的cosine值,这将作为我们计算高光时的因子,当α大于90度时候,cosine值为负值,此时没有高光效果。

      为了计算α,我们需要两个向量 'R' 和 'V'。 'V'向量可以用摄像机(视点)位置减光照作用的顶点位置得到,注意这2个位置应该都是位于世界坐标系。对于摄像机,我们需要在shader中传入它的世界坐标系位置,而高光的计算我们放在片元shader中,将对每个片元的世界坐标系位置(由插值而来)计算高光。 

     下面我们看看如何根据入射光向量I计算光线反射向量 'R':

      我们知道,向量并没有起始点,唯一决定向量的是方向和长度,所以,如上图所示,入射向量I可以看作“拷贝”到-N的方向的I,这时有I+V=R,(注意这儿的V并不是视点到顶点的向量,而是计算R的向量,也称作V有点混淆...),我们知道V/2 = N*(-N*I)【就是说V向量的和N向量一个方向,它的长度等于(-N*I)】,所有我们有了以下的公式:

     在GLSL中,有个内置的函数'reflect'就是用来计算反射向量的,在下面shader代码中,我们就用了该函数。

我们来看下最终的高光函数:

      它等于光源的颜色乘以物体表面的颜色,再乘以材质高光强度('M'),接着再乘以反射光线和摄像机指向物体的向量夹角的cosin值的p次方。如果物体没有高光效果,比如木头,则M为0,而p则被称为高光指数,它通常也是作为物体的材质属性,它的大小将决定高光区域边缘的显示效果,下面的图是p为1时候的高光效果:

而下面这幅图则是p为32时候的效果:

主要代码:

lighting_technique.h

class LightingTechnique : public Technique
{
public:
...
    void SetEyeWorldPos(const Vector3f& EyeWorldPos);
    void SetMatSpecularIntensity(float Intensity);
    void SetMatSpecularPower(float Power);
private:
...
    GLuint m_eyeWorldPosLocation;
    GLuint m_matSpecularIntensityLocation;
    GLuint m_matSpecularPowerLocation;

      我们在LightingTechnique类中增加了3个新的属性:眼的位置,高光强度和高光指数,这些都在光源中设置的,但是这样的设置并不太好,比如场景中的不同物质,可能高光效果是一样的,通常我们会把高光强度和高光指数当作材质属性,后面的教程中,我们会看到,这些属性会被当作顶点属性。

lighting_technique.cpp

out vec3 WorldPos0;
void main()
{
    gl_Position = gWVP * vec4(Position, 1.0);
    TexCoord0 = TexCoord;
    Normal0 = (gWorld * vec4(Normal, 0.0)).xyz;
    WorldPos0 = (gWorld * vec4(Position, 1.0)).xyz;
}

顶点shader增加了一行代码,用来得到顶点世界坐标系中的位置,以便在片元shader中计算高光时候使用。

in vec3 WorldPos0;
.
.
.
uniform vec3 gEyeWorldPos;
uniform float gMatSpecularIntensity;
uniform float gSpecularPower;
void main()
{
    vec4 AmbientColor = vec4(gDirectionalLight.Color, 1.0f) * gDirectionalLight.AmbientIntensity;
    vec3 LightDirection = -gDirectionalLight.Direction;
    vec3 Normal = normalize(Normal0);
    float DiffuseFactor = dot(Normal, LightDirection);
    vec4 DiffuseColor = vec4(0, 0, 0, 0);
    vec4 SpecularColor = vec4(0, 0, 0, 0);
    if (DiffuseFactor > 0) {
        DiffuseColor = vec4(gDirectionalLight.Color, 1.0f) *
            gDirectionalLight.DiffuseIntensity *
            DiffuseFactor;
        vec3 VertexToEye = normalize(gEyeWorldPos - WorldPos0);
        vec3 LightReflect = normalize(reflect(gDirectionalLight.Direction, Normal));
        float SpecularFactor = dot(VertexToEye, LightReflect);
        SpecularFactor = pow(SpecularFactor, gSpecularPower);
        if (SpecularFactor > 0) {
            SpecularColor = vec4(gDirectionalLight.Color, 1.0f) * gMatSpecularIntensity * SpecularFactor;
        }
    }
    FragColor = texture2D(gSampler, TexCoord0.xy) * (AmbientColor + DiffuseColor + SpecularColor);
}

      在片元shader中,我们增加了三个uniform变量,它们保存了眼的位置,高光强度以及高光指数,用来计算高光。环境光和前面教程中计算的方法一样,漫反射光和高光则被初始化为0,当入射光和物体表面夹角小于90度时,分别计算漫反射光和高光。计算高光时候,我们归一化了光线的方向向量和摄像机到像素的向量,最后根据公式得到高光产生的颜色。输出像素颜色时候,我们把纹理采样的颜色和光照的颜色进行混合调制操作,得到最终的颜色。

tutorial19.cpp

m_pEffect->SetEyeWorldPos(m_pGameCamera->GetPos());
m_pEffect->SetMatSpecularIntensity(1.0f);
m_pEffect->SetMatSpecularPower(32);

在render循环冲,我们设置摄像机位置,高光强度和高光指数。

下面是程序运行后的效果,我们可以选择物体观察高光的效果:

时间: 2024-10-29 04:08:18

opengl 教程(19) 高光的相关文章

NeHe的OpenGL教程6(Bang翻译Delphi版)-如何用图片进行纹理映射

NeHe的OpenGL教程6(Bang翻译Delphi版)-如何用图片进行纹理映射 在这一课里,我将教会你如何把纹理映射到立方体的六个面,如下图: 将下图放在应用程序data目录下,起名NeHe.bmp program lesson6a; {    OpenGL DelphiXE    出处:根据NeHe代码翻译而来(http://nehe.gamedev.net/)    作者:帅宏军 shuaihj@163.com     注:本单元用到了glaux.dll和glaux.pas,下载地址为:

NeHe的OpenGL教程1(Bang翻译Delphi版)-如何绘制OpenGL窗口

NeHe的OpenGL教程1(Bang翻译Delphi版)-如何绘制OpenGL窗口 在这个教程里,我将教你在Windows环境中创建OpenGL程序.它将显示一个空的OpenGL窗口,可以在窗口和全屏模式下切换,按ESC退出.它是我们以后应用程序的框架.如下图: program lesson1a; {    OpenGL DelphiXE    出处:根据NeHe代码翻译而来(http://nehe.gamedev.net/)    作者:帅宏军 shuaihj@163.com} uses 

NeHe的OpenGL教程2(Bang翻译Delphi版)-如何绘制平面图形

NeHe的OpenGL教程2(Bang翻译Delphi版)-如何绘制平面图形 这一课中,我将教您如何创建三角形和四边形.如下图: program lesson2a; {    OpenGL DelphiXE    出处:根据NeHe代码翻译而来(http://nehe.gamedev.net/)    作者:帅宏军 shuaihj@163.com} uses  Windows,  Messages,  OpenGL; // 全局变量var  h_Rc: HGLRC;               

NeHe的OpenGL教程5(Bang翻译Delphi版)-如何绘制立方体

NeHe的OpenGL教程5(Bang翻译Delphi版)-如何绘制立方体 在这一课里,我们把三角形变为立体的金子塔形状,把四边形变为立方体,如下图: program lesson5a; {    OpenGL DelphiXE    出处:根据NeHe代码翻译而来(http://nehe.gamedev.net/)    作者:帅宏军 shuaihj@163.com} uses  Windows,  Messages,  OpenGL; // 全局变量var  h_Rc: HGLRC;    

NeHe的OpenGL教程4(Bang翻译Delphi版)-如何让图形旋转

NeHe的OpenGL教程4(Bang翻译Delphi版)-如何让图形旋转 在这一课里,我将教会你如何旋转三角形和四边形.左图中的三角形沿Y轴旋转,四边形沿着X轴旋转.如下图 program lesson4a; {    OpenGL DelphiXE    出处:根据NeHe代码翻译而来(http://nehe.gamedev.net/)    作者:帅宏军 shuaihj@163.com} uses  Windows,  Messages,  OpenGL; // 全局变量var  h_Rc

NeHe的OpenGL教程7(Bang翻译Delphi版)-如何使用光源

NeHe的OpenGL教程7(Bang翻译Delphi版)-如何使用光源        在这一课里,我将教会你如何用光源照亮立方体的六个面,如下图: 将下图放在应用程序data目录下,起名NeHe.bmp program lesson7a; {    OpenGL DelphiXE    出处:根据NeHe代码翻译而来(http://nehe.gamedev.net/)    作者:帅宏军 shuaihj@163.com     在这一课里,我将教会你如何用光源照亮立方体的六个面     注:本

opengl 教程(17) 环境光

原帖地址:http://ogldev.atspace.co.uk/www/tutorial17/tutorial17.html       在3D真实感图形学中,光照是很重要的技术.从物理上讲,一束光是由很多细小的粒子"光子"组成,这些光子在空气中传输,在物体的表面折射,反射,最终进入我的视觉系统,形成了我们眼中看到的真实世界.在编程中,我们不可能模拟所有光子的行为,所以如何对光照进行建模,模拟出它的真实效果,是计算机图形学中一个永恒的话题       在各种paper中,人们已经提出

Dreamweaver MX 2004视频宝典教程(19)

dreamweaver|教程 第 19 集:Dreamweaver设计面板组 课程目标:学会Dreamweaver设计面板组的使用. 课程要点:Dreamweaver MX 2004设计面板组包括CSS样式面板和层面板.本节将简要介绍两个面板的使用方法. [全屏观看] | [下载视频] |

Photoshop教程:绘制高光的玻璃器皿

玻璃器皿的制作,关键是高光和暗调部分的处理.本教程介绍的方法非常不错,用选区工具把高光及暗调部分的选区单独勾出来,然后分别用调色工具调亮或变暗.后期再用涂抹工具修整好边缘即可.最终效果 1.新建一个600 * 800 像素的文档,背景填充暗灰色.新建一个图层,选择矩形选框工具,在属性栏把半径设置为10,然后拉出下图所示的圆角矩形选区填充颜色:#7A7977. 2.保持选区不变,选择菜单:选择 > 修改 > 收缩,数值为25,确定后按Ctrl + L 调整色阶,参数设置如下图. [1] [2]