• opengl 教程(19) 高光


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

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

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

    specular_light

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

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

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

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

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

    reflected_light

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

    reflect

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

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

    specular1

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

    shininess_1

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

    shininess_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循环冲,我们设置摄像机位置,高光强度和高光指数。

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

    clipboard

  • 相关阅读:
    go-elasticsearch 来自官方的 golang es client
    nginx unit nodejs 模块试用(续)
    WebSocket-Over-HTTP Protocol
    pushpin Server-sent events && openresty 集成试用
    Rendering on the Web
    pushpin openresty 集成试用
    100 webhook implementations
    streamdataio 实时数据分发平台
    Generic Realtime Intermediary Protocol
    Pushpin How it works
  • 原文地址:https://www.cnblogs.com/mikewolf2002/p/2877506.html
Copyright © 2020-2023  润新知