精华内容
参与话题
问答
  • 2 泛光模型3 Lambert漫反射模型3 Phong反射模型4 Blinn-Phong反射模型5 Reference 到了这一部分,我们就开始进入到着色(shading)的环节了,简单来说shading就是计算出每个采样点的颜色是多少,特别来说,本文所...

    (本篇文章同步发表于知乎专栏:https://zhuanlan.zhihu.com/p/144331612 欢迎三连关注)

    到了这一部分,我们就开始进入到着色(shading)的环节了,简单来说shading就是计算出每个采样像素点的颜色是多少。本文所要介绍的是局部光照模型,并不是真正准确的模型,但是优点是计算快,效果可以接受,至今依然广泛的运用在各种游戏之中。具体会从最基础的泛光模型,Lambert漫反射模型,再到Phong反射模型,Blinn-Phong反射模型一步步推进详细详解我们如何得到一个局部光照模型。

    1 我们为何能够看到物体?

    在进入具体模型讲解之时,我们以该章标题的问题作为一个开始,其实问题的答案我想大家都知道,之所以物体能被我们观察,是因为人眼接收到了从物体来的光。 没错,这其实也就是局部光照模型的基础,可以具体看看究竟有几种类型光线能从物体到人眼呢?

    如上图,可以先将光线简单的分为3类:

    1. 镜面反射
    2. 漫反射
    3. 环境光

    好,明白以上分类之后我们便进行具体模型的讲解!

    (Note:本文中光强,光的亮度,光的能量皆指同一个概念,目前阶段暂不做区分,另外所有方向向量都为单位向量

    2 泛光模型

    第一个, 泛光模型即只考虑环境光,这是最简单的经验模型,只会去考虑环境光的影响,并且不会去精确的描述,而只是用一个简单的式子表示

    其中KaK_a代表物体表面对环境光的反射率,IaI_a代表入射环境光的亮度,IenvI_{env}存储结果,即人眼所能看到从物体表面反射的环境光的亮度。效果如下:

    没错,泛光模型只能让我们看到一个物体的平面形状,怎么能够有体积感呢,这就要添加漫反射了,即Lambert漫反射模型。

    (tips:其中反射率还是光的亮度都是一个3维的RGB向量,为什么一个物体能够有颜色,其实就是它吸收了一定颜色的光,将剩下的光反射出来,也就有了颜色)

    3 Lambert漫反射模型

    所谓Lambert漫反射模型其实就是在泛光模型的基础之上增加了漫反射项。漫反射便是光从一定角度入射之后从入射点向四面八方反射,且每个不同方向反射的光的强度相等,而产生漫反射的原因是物体表面的粗糙,导致了这种物理现象的发生。

    这种漫反射我们该去怎么模拟呢?首先应该考虑入射的角度所造成的接收到光强的损失,如下图所示:

    只有当入射光线与平面垂直的时候才能完整的接受所有光的能量,而入射角度越倾斜损失的能量越大,具体来说,我们应该将光强乘上一个cosθ=lncos\theta = l \cdot n,其中ll是入射光方向,nn为平面法线方向。

    好了!除了入射角度之外,光源与照射点的距离也应该考虑,直观来说,离得越远当然强度也就越弱!具体来说如图所示:

    图中中心为一个点光源,光线均匀的向周围发射,可以想象光源发射出来的能量其实是一定的,那么在任意两个圈上接受到的能量之和一定相等。而离圆心越远,圆的面积越大,单位面积所接受能量也就越弱,因此会将光强 II 除上一个 r2r^2

    OK,如此我们便可以较为正确的去模拟漫反射了!如下式:

    其中kdk_d为漫反射系数,II入射光强,n,ln,l分别如图中所示为法线向量和入射方向,max是为了剔除夹角大于90°的光。
    注意漫反射光线强度是与出射方向无关的,因此无论人眼在哪观察接收到的强度都是一样的!
    将环境光与漫反射一起考虑之后:

    因为漫反射的存在我们已经能够很明显的看出茶壶的体积感了,但依然感觉不是很真实,因为缺少了高光!即镜面反射,下一节将介绍在Lambert模型之内再加入镜面反射,从而得到Phong模型!

    (tips:通过改变漫反射模型的3维反射系数kdk_d,我们就能够得到物体表面不同的颜色)

    3 Phong反射模型

    相信所有读者都对镜面反射十分了解,这是我们从小学就知道的物理知识了!如图所示RR为镜面反射方向,vv为人眼观察方向。

    除了考虑漫反射中提到的光源到反射点的距离rr之外,需要注意的是,观察方向在镜面反射时是很重要的,具体来说,只有当观察方向集中在反射方向周围很近的时候才能看见反射光,因此在镜面反射中会考虑 RRvv 的夹角 α\alpha。如下式:
    在这里插入图片描述
    其中ksk_s为镜面反射系数,II为入射光强,rr为光源到入射点距离,注意这里在max剔除大于90°的光之后,我们还乘了一个指数p,添加该项的原因很直接,因为离反射光越远就越不应该看见反射光,需要一个指数p加速衰减
    在这里插入图片描述
    最后我们把环境光,漫反射光,镜面反射光全部累加得到Phong模型效果:
    在这里插入图片描述
    可以看出,此时模型其实已经非常接近真实效果了!那么Blinn-Phong反射模型是什么呢?它只是对phong模型计算反射方向与人眼观察方向角度的一个优化!

    4 Blinn-Phong反射模型

    如上文所提,我们将反射方向与人眼观察方向夹角替换成如下图所示的一个半程向量和法线向量的夹角
    在这里插入图片描述
    这样的得到的结果其实是与真实计算反射与人眼观察夹角的结果是非常近似的(具体来说该角度是正确角度的一半),但好处在于大大加速了角度计算的速度,提升了效率!

    读者可以自己试试计算半程向量与反射向量谁快(加法次数,乘法次数比较)
    (tips:反射向量可由入射方向在法线方向投影的两倍减去入射方向得出)

    整体计算公式:
    在这里插入图片描述

    5 着色方法(频率)

    在上文中我们讲解完了局部光照模型,其中主要利用了观察方向,入射光线与法线向量的位置关系,但并没有具体说究竟是三角形面的法线向量还是三角形顶点的法线向量,这也就牵扯出了本章内容——着色频率(面着色,顶点着色,像素着色),这3种不同的着色频率其实也就对应了三种不同方法。接下来一一介绍

    5.1 Flat Shading

    面着色,顾名思义以每一个面作为一个着色单位。模型数据大多以很多个三角面进行存储,因此也就记录了每个面的法线向量,利用每个面的法线向量进行一次Blinn-Phong反射光照模型的计算,将该颜色赋予整个面,效果如下:

    Flat Shading 虽然计算很快,只需对每一个面进行一次着色计算,但是效果确是很差的,可以很明显的看到一块块面形状。因此一种改进方法就是对三角形面的每个顶点进行着色,再对三角形面内的颜色插值,即Gouraud Shading。

    5.2 Gouraud Shading

    Gouraud Shading会对每个三角形的顶点进行一次着色,那么首当其冲的问题便是,我们只有每个面的法线向量,如何得到每个顶点的法线向量呢。做法其实很简单,将所有共享这个点的面的法线向量加起来求均值,最后再标准化就得到了该顶点的法线向量了。

    有了每个三角形的顶点向量之后,自然就可以计算出每个顶点的颜色了,那么对于三角形内部的每一个点应该怎么办呢?对,就是利用我们在计算机图形学三中所提到了重心坐标来插值了!公式如下:

    其中c0,c1,c2c_0,c_1,c_2为三角形三个顶点的颜色,α,β,γ\alpha,\beta,\gamma为三角形面内一点的重心坐标,cc为该点的插值之后得到的颜色。

    这样就能成功的得到每一个点的颜色了,效果如下:

    可以明显看出相对于Flat Shading,Gouraud Shading的效果有着明显的提升,但这样依然还不是最好的做法,因为我们实际上只对每个三角形顶点进行了着色,然后其它的颜色都是通过插值得到,有没有一种做法可以真正的对每个点用Blinn-Phong模型计算得出颜色呢?没错,那就是Phong Shading了!

    (tips:1.这里有两个tips可以注意一下,首先重心坐标一定要是原世界坐标空间中的重心坐标,但实际计算中一般会使用投影之后的二维平面来计算重心坐标,存在着一个误差需要校正,这会在下一节笔记中展开来谈。
    2. 第二点,其实按理来说Gouraud用的是双线性插值(会在之后的贝塞尔曲线中具体讲解),但是道理都是相同的,本文这里为了方便就直接用了重心坐标插值)

    5.3 Phong Shading

    Phong Shading的做法其实也是很好理解的,既然要对每个点都进行光照计算,那么自然我们应该要有每个点的法线向量才可以,在第2章中,我们提到了如何得到每个顶点的法线向量,那么对于三角形内部的每一个点的法线向量自然也可以像插值颜色一般得到:

    其中n0,n1,n2\mathbf{n_0},\mathbf{n_1},\mathbf{n_2}分别是三角形三个顶点的法线向量,α,β,γ\alpha,\beta,\gamma为三角形面内一点的重心坐标,n\mathbf{n}为该点插值之后得到的法线向量。如此便得到了任意一点的法线向量了,也当然可以对任意一点进行Blinn-Phong模型的计算了。最终对比渲染效果如下:

    可以明显看出Phong Shading对于高光的显示相比于Gouraud Shading是更真实的。有一点的要注意的是,这里所有的茶壶所使用的都是一个低精度模型,这点可以从Flat Shading的结果可以看出,面片是有限的,那么如果随着模型精度的提升,各种shading type又会有怎样的区别呢,这里给出一幅大图供读者思考

    Note:变换法线向量

    有的读者可能会疑问,法线向量不是一直存在于世界坐标空间之中吗,为什么要去变换他呢,其实原因很简单,因为模型变换可能会导致模型位置形状发生改变,如果属于该模型的各个三角形面的法线向量不跟着改变的话,那么此时所记录的法线向量就是错误的。因此法线向量一定也要跟着模型本身发生改变。

    那么是否简单的将作用在模型本身的变换也作用在对应的法线向量之上就可以了呢?答案是否定的,这也是为什么要在这里探讨这个问题,如下图所示,一个简单矩形经过一个剪切变换M之后:

    在经过一个简单的剪切变换之后,不难发现矩形右边的法线向量没有改变,如果此时简单的用变换矩阵MnM\cdot n得到的结果就是图中的所标注为Mn的向量,并不是与该面垂直的法线向量,那么对应的真正的变换矩阵应该是什么呢?
    首先定义tM=Mtt_M = Mt是该面上任意一向量变换之后的结果, nN=Nnn_N = Nn表示经过变换之后正确的法线向量,即真正的对法线的变换矩阵为NN,目的就是求出这个N。作如下推导:


    简单来说就是加入了M1MM^{-1}M做了一个恒等变换,但是可以把Mt单独提出来为tMt_M

    在这里插入图片描述
    到这一步就很明显了,什么向量与变换之后的原来面上的向量垂直呢? 法线向量! 即上式中的nTM1n^TM^{-1}即为变换之后的法线向量的转置(转置是因为点乘的缘故)。进一步我们便可以找出NN是多少
    在这里插入图片描述
    至此应该很轻松就能看出N=(M1)TN = (M^{-1}) ^T了,推导结束!

    总结

    至此我们知道如何计算变换之后的法线向量,又知道了如何插值出每个点的法线向量,已经可以利用上节的Blinn-Phong模型渲染出相当质量的图形了,但还有一点正如文中的tips所提到的,那就是对重心坐标插值误差的一个纠正,这究竟是个什么问题呢,我们在下一节当中进行具体探讨!

    最后如果本文对你有帮助求点赞求收藏求一个大大的关注 😃(如果没啥用就算了 😦),后序会持续更新,感谢阅读!

    Reference

    [1] Fundamentals of Computer Graphics 4th
    [2] GAMES101-现代计算机图形学入门-闫令琪
    [3] 电子科技大学计算机图形学PPT

    展开全文
  • 基础光照-Phong 光照模型及其实现

    千次阅读 2019-09-18 22:19:08
    1. Phong 光照模型 1.1 环境光照 Ambient Lighting 1.1.1 实现代码 1.2 漫反射光照 Diffuse Lighting 1.2.1 法向量 及实现代码 1.2.2 计算漫反射光照 1.2.3 法向量 注意事项 1.3 镜面光照 ...

     

    1. Phong 光照模型

    现实世界的光照是极其复杂的,而且会受到诸多因素的影响,这是我们有限的计算能力所无法模拟的。因此OpenGL的光照使用的是简化的模型,对现实的情况进行近似,这样处理起来会更容易一些,而且看起来也差不多一样。这些光照模型都是基于我们对光的物理特性的理解。其中一个模型被称为冯氏光照模型(Phong Lighting Model)。冯氏光照模型的主要结构由3个分量组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照。下面这张图展示了这些光照分量看起来的样子:

    • 环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。
    • 漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。
    • 镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。

    注意这里的前提是使用的是点光源。

    为了创建有趣的视觉场景,我们希望模拟至少这三种光照分量。我们将以最简单的一个开始:环境光照

    1.1 环境光照 Ambient Lighting

    光通常都不是来自于同一个光源,而是来自于我们周围分散的很多光源,即使它们可能并不是那么显而易见。光的一个属性是,它可以向很多方向发散并反弹,从而能够到达不是非常直接临近的点。所以,光能够在其它的表面上反射,对一个物体产生间接的影响。考虑到这种情况的算法叫做全局照明(Global Illumination)算法,但是这种算法既开销高昂又极其复杂。

    由于我们现在对那种又复杂又开销高昂的算法不是很感兴趣,所以我们将会先使用一个简化的全局照明模型,即环境光照。正如你在上一节所学到的,我们使用一个很小的常量(光照)颜色,添加到物体片段的最终颜色中,这样子的话即便场景中没有直接的光源也能看起来存在有一些发散的光。

    Phong光照模型中的环境光照(Ambient lighting)部分就是模拟全局光照中间接光照的影响(即来自于其他物体的反射光等),这里只是很简单的近似,因此结果很粗糙。

    1.1.1 实现代码

    把环境光照添加到场景里非常简单。我们用光的颜色乘以一个很小的常量环境因子,再乘以物体的颜色,然后将最终结果作为片段的颜色:

    void main()
    {
        float ambientStrength = 0.1;
        vec3 ambient = ambientStrength * lightColor;
    
        vec3 result = ambient * objectColor;
        FragColor = vec4(result, 1.0);
    }

    1.2 漫反射光照 Diffuse Lighting

    环境光照本身不能提供最有趣的结果,但是漫反射光照就能开始对物体产生显著的视觉影响了。漫反射光照使物体上与光线方向越接近的片段能从光源处获得更多的亮度

    有一个光源,它所发出的光线落在物体的一个片段上。我们需要测量这个光线是以什么角度接触到这个片段的。如果光线垂直于物体表面,这束光对物体的影响会最大化(译注:更亮)。为了测量光线和片段的角度,我们使用一个叫做法向量(Normal Vector) 的东西,它是垂直于片段表面的一个向量(这里以黄色箭头表示),我们在后面再讲这个东西。这两个向量之间的角度很容易就能够通过点乘计算出来。

    我们知道两个单位向量的夹角越小,它们点乘的结果越倾向于1。当两个向量的夹角为90度的时候,点乘会变为0。这同样适用于$θ$$θ$越大,光对片段颜色的影响就应该越小。

    我们知道两个单位向量的夹角越小,它们点乘的结果越倾向于1。当两个向量的夹角为90度的时候,点乘会变为0。这同样适用于$θ$$θ$越大,光对片段颜色的影响就应该越小。

    点乘返回一个标量,我们可以用它计算光线对片段颜色的影响。不同片段朝向光源的方向的不同,这些片段被照亮的情况也不同。

    所以,计算漫反射光照需要什么?

    • 法向量:一个垂直于顶点表面的向量。
    • 定向的光线:作为光源的位置与片段的位置之间向量差的方向向量。为了计算这个光线,我们需要光的位置向量和片段的位置向量。

    1.2.1 法向量 及实现代码

    法向量是一个垂直于顶点表面的(单位)向量。由于顶点本身并没有表面(它只是空间中一个独立的点),我们利用它周围的顶点来计算出这个顶点的表面。我们能够使用一个小技巧,使用叉乘对立方体所有的顶点计算法向量,但是由于3D立方体不是一个复杂的形状,所以我们可以简单地把法线数据手工添加到顶点数据中。试着去想象一下,这些法向量真的是垂直于立方体各个平面的表面的(一个立方体由6个平面组成)。

    由于我们向顶点数组添加了额外的数据,所以我们应该更新光照的顶点着色器:

    #version 330 core
    layout (location = 0) in vec3 position;
    layout (location = 1) in vec3 normal;
    ...

    现在我们已经向每个顶点添加了一个法向量并更新了顶点着色器,我们还要更新顶点属性指针。注意,灯使用同样的顶点数组作为它的顶点数据,然而灯的着色器并没有使用新添加的法向量。我们不需要更新灯的着色器或者是属性的配置,但是我们必须至少修改一下顶点属性指针来适应新的顶点数组的大小:

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    我们只想使用每个顶点的前三个float,并且忽略后三个float,所以我们只需要把步长参数改成float大小的6倍就行了。

    虽然对光源的着色器使用不能完全利用的顶点数据看起来不是那么高效,但这些顶点数据已经从箱子对象载入后开始就储存在GPU的内存里了,所以我们并不需要储存新数据到GPU内存中。这实际上比给光源专门分配一个新的VBO更高效了。

    所有光照的计算都是在片段着色器里进行,所以我们需要将法向量由顶点着色器传递到片段着色器。我们这么做:

    后期使用: 更新后的顶点着色器的代码如下所示:

    #version 330 core
    layout (location = 0) in vec3 position;
    layout (location = 1) in vec3 normal;
    
    uniform mat4 projection;
    uniform mat4 view;
    uniform mat4 model;
    
    out vec3 Normal;
    
    void main()
    {
        gl_Position = projection * view * model * vec4(position, 1.0);
        Normal = normal;
    }

    接下来的,在片段着色器中定义相应的输入变量:

    in vec3 Normal;

    1.2.2 计算漫反射光照

    我们现在对每个顶点都有了法向量,但是我们仍然需要光源的位置向量片段的位置向量。由于光源的位置是一个静态变量,我们可以简单地在片段着色器中把它声明为uniform:

    uniform vec3 lightPos;

    然后在渲染循环中(渲染循环的外面也可以,因为它不会改变)更新uniform。我们使用在前面声明的lightPos向量作为光源位置:

    lightingShader.setVec3("lightPos", lightPos);

    最后,我们还需要片段的位置。我们会在世界空间中进行所有的光照计算,因此我们需要一个在世界空间中的顶点位置。我们可以通过把顶点位置属性乘以模型矩阵(不是观察和投影矩阵)来把它变换到世界空间坐标。这个在顶点着色器中很容易完成,所以我们声明一个输出变量,并计算它的世界空间坐标:

    out vec3 FragPos;  
    out vec3 Normal;
    
    void main()
    {
        gl_Position = projection * view * model * vec4(position, 1.0);
        FragPos = vec3(model * vec4(position, 1.0));
        Normal = normal;
    }

    最后,在片段着色器中添加相应的输入变量。

    in vec3 FragPos;

    现在,所有需要的变量都设置好了,我们可以在片段着色器中添加光照计算了。

    我们需要做的第一件事是计算光源和片段位置之间的方向向量。前面提到,光的方向向量是光源位置向量与片段位置向量之间的向量差。我们能够简单地通过让两个向量相减的方式计算向量差。我们同样希望确保所有相关向量最后都转换为单位向量,所以我们把法线和最终的方向向量都进行标准化:

    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(lightPos - FragPos);

    当计算光照时我们通常不关心一个向量的模长或它的位置,我们只关心它们的方向。所以,几乎所有的计算都使用单位向量完成,因为这简化了大部分的计算(比如点乘)。所以当进行光照计算时,确保你总是对相关向量进行标准化,来保证它们是真正地单位向量。忘记对向量进行标准化是一个十分常见的错误。

    下一步,我们对normlightDir向量进行点乘,计算光源对当前片段实际的漫反射影响。结果值再乘以光的颜色,得到漫反射分量。两个向量之间的角度越大,漫反射分量就会越小:

    float diff = max(dot(norm, lightDir), 0.0);
    vec3 diffuse = diff * lightColor;

    如果两个向量之间的角度大于90度,点乘的结果就会变成负数,这样会导致漫反射分量变为负数。为此,我们使用max函数返回两个参数之间较大的参数,从而保证漫反射分量不会变成负数。负数颜色的光照是没有定义的,所以最好避免它,除非你是那种古怪的艺术家。

    现在我们有了环境光分量和漫反射分量,我们把它们相加,然后把结果乘以物体的颜色,来获得片段最后的输出颜色。

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

    尝试在你的脑中想象一下法向量,并在立方体周围移动,注意观察法向量和光的方向向量之间的夹角越大,片段就会越暗。

    后期使用: 更新后完整的顶点着色器的代码如下:

    #version 330 core
    layout (location = 0) in vec3 position;
    layout (location = 1) in vec3 normal;
    
    uniform mat4 projection;
    uniform mat4 view;
    uniform mat4 model;
    
    out vec3 Normal;
    out vec3 FragPos;
    
    void main()
    {
        FragPos = vec3(model * vec4(position, 1.0));
        Normal = normal;
    
        gl_Position = projection * view * vec4(FragPos, 1.0);
    }

    更新后完整的片段着色器的代码如下:

    #version 330 core
    out vec4 FragColor;
    
    in vec3 Normal;  
    in vec3 FragPos;  
    
    uniform vec3 lightPos; 
    uniform vec3 lightColor;
    uniform vec3 objectColor;
    
    vec3 calculateLighting(){
         // ambient
        float ambientStrength = 0.1;
        vec3 ambient = ambientStrength * lightColor;
    
        // diffuse 
        vec3 norm = normalize(Normal);
        vec3 lightDir = normalize(lightPos - FragPos);
        float diff = max(dot(norm, lightDir), 0.0);
        vec3 diffuse = diff * lightColor;
    
        return (diffuse + ambient);
    }
    
    void main()
    {
    
        vec3 result = objectColor * calculateLighting();
        FragColor = vec4(result, 1.0);
    } 

    在main.cpp中,添加如下代码:

    lightingShader.setVec3("lightPos", lightPos);
    lightingShader.setVec3("lightColor", lightColor);
    lightingShader.setVec3("objectColor", objectColor);

    Phong光照模型中的漫反射光照(diffuse lighting)部分就是模拟片段法向量与光源方向向量夹角之间的关系,漫反射光照使物体上与光线方向越接近的片段能从光源处获得更多的亮度

    1.2.3 法向量 注意事项

    现在我们已经把法向量从顶点着色器传到了片段着色器。可是,目前片段着色器里的计算都是在世界空间坐标中进行的。所以,我们是不是应该把法向量也转换为世界空间坐标?基本正确,但是这不是简单地把它乘以一个模型矩阵就能搞定的。

    首先,法向量只是一个方向向量,不能表达空间中的特定位置。同时,法向量没有齐次坐标(顶点位置中的w分量)。这意味着,位移不应该影响到法向量。因此,如果我们打算把法向量乘以一个模型矩阵,我们就要从矩阵中移除位移部分,只选用模型矩阵左上角3×3的矩阵(注意,我们也可以把法向量的w分量设置为0,再乘以4×4矩阵;这同样可以移除位移)。对于法向量,我们只希望对它实施缩放和旋转变换。

    其次,如果模型矩阵执行了不等比缩放,顶点的改变会导致法向量不再垂直于表面了。因此,我们不能用这样的模型矩阵来变换法向量。

    每当我们应用一个不等比缩放时(注意:等比缩放不会破坏法线,因为法线的方向没被改变,仅仅改变了法线的长度,而这很容易通过标准化来修复),法向量就不会再垂直于对应的表面了,这样光照就会被破坏。

    修复这个行为的诀窍是使用一个为法向量专门定制的模型矩阵。这个矩阵称之为法线矩阵(Normal Matrix),它使用了一些线性代数的操作来移除对法向量错误缩放的影响。如果你想知道这个矩阵是如何计算出来的,建议去阅读这个文章

    法线矩阵被定义为「模型矩阵左上角的逆矩阵的转置矩阵」。真是拗口,如果你不明白这是什么意思,别担心,我们还没有讨论逆矩阵(Inverse Matrix)和转置矩阵(Transpose Matrix)。注意,大部分的资源都会将法线矩阵定义为应用到模型-观察矩阵(Model-view Matrix)上的操作,但是由于我们只在世界空间中进行操作(不是在观察空间),我们只使用模型矩阵。

    在顶点着色器中,我们可以使用inverse和transpose函数自己生成这个法线矩阵,这两个函数对所有类型矩阵都有效。注意我们还要把被处理过的矩阵强制转换为3×3矩阵,来保证它失去了位移属性以及能够乘以vec3的法向量。

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

    在漫反射光照部分,光照表现并没有问题,这是因为我们没有对物体本身执行任何缩放操作,所以并不是必须要使用一个法线矩阵,仅仅让模型矩阵乘以法线也可以。可是,如果你进行了不等比缩放,使用法线矩阵去乘以法向量就是必不可少的了。

    即使是对于着色器来说,逆矩阵也是一个开销比较大的运算,因此,只要可能就应该避免在着色器中进行逆矩阵运算,它们必须为你场景中的每个顶点都进行这样的处理。用作学习目这样做是可以的,但是对于一个对效率有要求的应用来说,在绘制之前你最好用CPU计算出法线矩阵,然后通过uniform把值传递给着色器(像模型矩阵一样)。

    1.3 镜面光照 Specular Lighting

    和漫反射光照一样,镜面光照也是依据光的方向向量和物体的法向量来决定的,但是它也依赖于观察方向,例如玩家是从什么方向看着这个片段的。镜面光照是基于光的反射特性。如果我们想象物体表面像一面镜子一样,那么,无论我们从哪里去看那个表面所反射的光,镜面光照都会达到最大化。

    我们通过反射法向量周围光的方向来计算反射向量。然后我们计算反射向量和视线方向的角度差,如果夹角越小,那么镜面光的影响就会越大。它的作用效果就是,当我们去看光被物体所反射的那个方向的时候,我们会看到一个高光

    观察向量是镜面光照附加的一个变量,我们可以使用观察者世界空间位置和片段的位置来计算它。之后,我们计算镜面光强度,用它乘以光源的颜色,再将它加上环境光和漫反射分量。

    我们选择在世界空间进行光照计算,但是大多数人趋向于在观察空间进行光照计算。在观察空间计算的好处是,观察者的位置总是(0, 0, 0),所以这样你直接就获得了观察者位置。可是我发现在学习的时候在世界空间中计算光照更符合直觉。如果你仍然希望在观察空间计算光照的话,你需要将所有相关的向量都用观察矩阵进行变换(记得也要改变法线矩阵)

    为了得到观察者的世界空间坐标,我们简单地使用摄像机对象的位置坐标代替(它当然就是观察者)。所以我们把另一个uniform添加到片段着色器,把相应的摄像机位置坐标传给片段着色器:

    uniform vec3 viewPos;
    lightingShader.setVec3("viewPos", camera.Position);

    现在我们已经获得所有需要的变量,可以计算高光强度了。首先,我们定义一个镜面强度(Specular Intensity) 变量,给镜面高光一个中等亮度颜色,让它不要产生过度的影响。

    float specularStrength = 0.5;

    如果我们把它设置为1.0f,我们会得到一个非常亮的镜面光分量,这对于一个珊瑚色的立方体来说有点太多了。下一节教程中我们会讨论如何合理设置这些光照强度,以及它们是如何影响物体的。下一步,我们计算视线方向向量,和对应的沿着法线轴的反射向量:

    vec3 viewDir = normalize(viewPos - FragPos);
    vec3 reflectDir = reflect(-lightDir, norm);

    需要注意的是我们对lightDir向量进行了取反。reflect函数要求第一个向量是从光源指向片段位置的向量,但是lightDir当前正好相反,是从片段指向光源(由先前我们计算lightDir向量时,减法的顺序决定)。为了保证我们得到正确的reflect向量,我们通过对lightDir向量取反来获得相反的方向。第二个参数要求是一个法向量,所以我们提供的是已标准化的norm向量。

    剩下要做的是计算镜面分量。下面的代码完成了这件事:

    float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
    vec3 specular = specularStrength * spec * lightColor;

    我们先计算视线方向与反射方向的点乘(并确保它不是负值),然后取它的32次幂。这个32是高光的反光度(Shininess)。一个物体的反光度越高,反射光的能力越强,散射得越少,高光点就会越小

    我们不希望镜面成分过于显眼,所以我们把指数保持为32。剩下的最后一件事情是把它加到环境光分量和漫反射分量里,再用结果乘以物体的颜色:

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

    我们现在为冯氏光照计算了全部的光照分量。

    后期使用:更新后完整的顶点着色器的代码如下(顶点着色器的代码没有变化):

    #version 330 core
    layout (location = 0) in vec3 position;
    layout (location = 1) in vec3 normal;
    
    uniform mat4 projection;
    uniform mat4 view;
    uniform mat4 model;
    
    out vec3 Normal;
    out vec3 FragPos;
    
    void main()
    {
        FragPos = vec3(model * vec4(position, 1.0));
        Normal = normal;
    
        gl_Position = projection * view * vec4(FragPos, 1.0);
    }

    完整的片段着色器的代码如下:

    #version 330 core
    out vec4 FragColor;
    
    in vec3 Normal;  
    in vec3 FragPos;  
    
    uniform vec3 lightPos;
    uniform vec3 viewPos;
    
    uniform vec3 lightColor;
    uniform vec3 objectColor;
    
    vec3 calculateLighting(){
         // ambient
        float ambientStrength = 0.1;
        vec3 ambient = ambientStrength * lightColor;
    
        // diffuse 
        vec3 norm = normalize(Normal);
        vec3 lightDir = normalize(lightPos - FragPos);
        float diff = max(dot(norm, lightDir), 0.0);
        vec3 diffuse = diff * lightColor;
    
        // specular
        float specularStrength = 0.5;
        vec3 viewDir = normalize(viewPos - FragPos);
        vec3 reflectDir = reflect(-lightDir, norm);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
        //vec3 specular = specularStrength * spec * lightColor;
        vec3 specular = spec * lightColor;
    
        return (diffuse + ambient + specular);
    }
    
    void main()
    {
    
        vec3 result = objectColor * calculateLighting();
        FragColor = vec4(result, 1.0);
    } 

    在main.cpp中,添加如下代码:

    lightingShader.setVec3("lightPos", lightPos);
    lightingShader.setVec3("viewPos", camera.Position);
    lightingShader.setVec3("lightColor", lightColor);
    lightingShader.setVec3("objectColor", objectColor);

    Phong光照模型中的镜面光照(specular lighting)部分就是模拟模型表面上出现的亮点,如果观察方向与光线方向的反射向量之间的夹角越小,表示受反射光(镜面光)的影响越大,其效果就是使得我们能够看到一个高光效果。

    2. 总结

    在光照着色器的早期,开发者曾经在顶点着色器中实现冯氏光照模型。在顶点着色器中做光照的优势是,相比片段来说,顶点要少得多,因此会更高效,所以(开销大的)光照计算频率会更低。然而,顶点着色器中的最终颜色值是仅仅只是那个顶点的颜色值,片段的颜色值是由插值光照颜色所得来的。结果就是这种光照看起来不会非常真实,除非使用了大量顶点。

     

    在顶点着色器中实现的冯氏光照模型叫做Gouraud着色(Gouraud Shading),而不是冯氏着色(Phong Shading)。记住,由于插值,这种光照看起来有点逊色。冯氏着色能产生更平滑的光照效果。

    总结一下:Phong光照模型提供了上面的环境光(ambient lighting)、漫反射光(diffuse lighting)和镜面反射光(specular lighting)这三种分量的计算方式,如果在顶点着色器中实现Phong光照模型的,叫做Gouraud着色(Gouraud Shading);如果在片段着色器中实现Phong光照模型的,叫做Phong着色(Phong Shading)

    Phong光照模型的各分量:
    1. 环境光(ambient lighting): 模拟全局光照中间接光照的影响(即来自于其他物体的反射光等),这里只是很简单的近似,因此结果很粗糙。
    2. 漫反射光(diffuse lighting): 模拟片段法向量与光源方向向量夹角之间的关系,漫反射光照使物体上与光线方向越接近的片段能从光源处获得更多的亮度。
    3. 镜面反射光(specular lighting): 模拟模型表面上出现的亮点,如果观察方向与光线方向的反射向量之间的夹角越小,表示受反射光(镜面光)的影响越大,其效果就是使得我们能够看到一个高光效果。:

    从Phong光照模型的计算方法来看,它提供了一个很简单的光照模型,对各种情况只是近似,但是实现的效果与其他的光照模型相差不大(追求真实的话,该光照模型不适合),比较适合入门了解的光照模型。

    展开全文
  • Phong与Blinn-Phong

    2019-07-15 19:37:36
    原理区别: phong使用视线与入射光线反射向量的夹角: blinn-phong使用halfway(视线与...2.由于blinn-phong所用计算的夹角往往小于phong所使用的夹角,因此blinn-phong的点积值更大,需要更大(往往为2-4倍)...

    原理区别:

    phong使用视线与入射光线反射向量的夹角:

    blinn-phong使用halfway(视线与入射光线的中间方向)与normal的夹角:

    效果区别:

    1.phong因为视线与光反射向量夹角大于90度时取0,因此有明显的边缘

    2.由于blinn-phong所用计算的夹角往往小于phong所使用的夹角,因此blinn-phong的点积值更大,需要更大(往往为2-4倍)的exp值才能达成与phong相似效果

    代码区别:

    void main()
    {
        [...]
        float spec = 0.0;
        if(blinn)
        {
            vec3 halfwayDir = normalize(lightDir + viewDir);  
            spec = pow(max(dot(normal, halfwayDir), 0.0), 16.0);
        }
        else
        {
            vec3 reflectDir = reflect(-lightDir, normal);
            spec = pow(max(dot(viewDir, reflectDir), 0.0), 8.0);
        }
    }

     

    展开全文
  • 7.Phong 和 BlinnPhong

    千次阅读 2016-07-07 18:39:51
    Lambert漫反射光照模型,这是一个用来模拟粗糙表面对光线的漫反射现象的经验模型,对于纸张、粗糙墙壁等等来说,这个模型或许够用,但对于金属这样的光滑表面来说,我们就需要使用Phong模型来模拟光滑表面对光线的...

    Lambert漫反射光照模型,这是一个用来模拟粗糙表面对光线的漫反射现象的经验模型,对于纸张、粗糙墙壁等等来说,这个模型或许够用,但对于金属这样的光滑表面来说,我们就需要使用Phong模型来模拟光滑表面对光线的镜面反射现象。同Lambert一样,这个模型也是经验模型,而且在程序中,我们经常同时使用Lambert和Phong两个模型,因为在现实世界中,任何表面都会同时发生漫反射和镜面反射两种现象,因此我们就要使用两种模型分别计算两种反射后的光强(也就是顶点颜色值),是渲染的效果看起来真实一些。但要注意,这样做并不会带来真正真实的渲染效果,毕竟这两种模型都是经验模型,考虑的都是理想情况下。而Blinn-phong光照模型是基于Phong的修正模型。

    设顶点的单位法向量为N,有公式:

    R + L = (2N • L)N   (这里再次提醒一下,L的方向是由顶点指向光源的)

    由这个可以推出:

    R = (2N • L)N - L

    因此Phong的公式如下图左图所示。


    imageimage

    Shader "Study/7_Phong" {
    	Properties{
    		_Color("Main Color", Color) = (1,1,1,1)
    		_SpecColor("Specular Color", Color) = (0.5, 0.5, 0.5, 1)// 高光颜色
    		_Shininess("Shininess", Range(0.01, 1)) = 0.078125// 高光指数【光泽度】
    		_MainTex("Base (RGB) Gloss (A)", 2D) = "white" {}
    	}
    		SubShader{
    		Tags{ "RenderType" = "Opaque" }
    		LOD 300
    
    		CGPROGRAM
    #pragma surface surf BlinnPhongTest
    		// 半角向量和BilnnPhong:使用入射光线和视线的中间平均值,即半角向量,然后使用该半角和法线计算出一个和视角相关的高光。
    		// 相关参数:【lightDir点到光源单位向量】【viewDir点到摄像机单位向量】【atten衰减系数】【_LightColor0场景中平行光的颜色】
    		float4 LightingBlinnPhongTest(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
    	{
    		// 1.半角向量:求(点到光源+点到摄像机)的单位向量,他们的中间平均值
    		float3 h = normalize(lightDir + viewDir);
    		// 2.漫反射系数【点到光源单位向量与法线向量的余弦值】
    		float diff = max(0, dot(s.Normal, lightDir));
    		// 3.反射向量
    		float3 r = 2 * dot(s.Normal, lightDir) * s.Normal - lightDir;
    		// 4.高光底数【反射向量与视角向量的余弦值】
    		float re = max(0, dot(r, viewDir));
    		// 5.高光系数:根据高光低数和高光指数求得
    		float spec = pow(re, s.Specular * 256) * s.Gloss;
    		float4 c;
    		// 6.最终光照rgb = 漫反射+半角高光
    		c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * _SpecColor.rgb * spec) * (atten * 2);
    		c.a = s.Alpha + _LightColor0.a * _SpecColor.a * spec * atten;
    		return c;
    	}
    	sampler2D _MainTex; // 主材质
    	fixed4 _Color;  // 主材质颜色
    	half _Shininess; // 高光指数
    	struct Input {
    		float2 uv_MainTex; // 主材质的UV信息
    	};
    	void surf(Input IN, inout SurfaceOutput o) {
    		// 取主纹理的对应当前像素点的值
    		fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
    
    		// Albedo反照率,即物体反射光的数量与外来光数量的比值。
    		// Albedo = 主纹理 x 主色调,反映了物体的基色,与任何光相关的信息(比如diffuse, shiness等)无关
    		o.Albedo = tex.rgb * _Color.rgb;    // 颜色纹理=主纹理*主材质颜色
    
    											// Gloss光滑度[0, 1],用于控制反射的模糊程度,值越大,高光反射越清晰,反之则越模糊,0将无高光效果。
    											// 光滑度的“滑”是面的概念,代表物体整体的光滑程度
    											// 比如说,同样一块金属,在它生锈的过程中,其反射就会慢慢变弱,可以通过Gloss值控制
    											// 实际上它是针对高光计算结果的附加系数
    		o.Gloss = tex.a;
    		o.Alpha = tex.a * _Color.a;      // 透明度
    										 // Shininess光泽度[0, 1],又叫高光指数或镜面反射指数,注意,它在SurfaceOutput结构中的命名(Specular)很容易让人误解为它是高光强度,其实不然,它是高光指数
    										 // 光泽度的“泽”是点的概念,代表物体某个高光点的光泽程度
    		o.Specular = _Shininess; // 越小光泽度越高,0为全白
    								 //o.Emission = tex.rgb;
    	}
    	ENDCG
    	}
    		Fallback "VertexLit"
    }


    相比较Phong模型,Blinn-phong模型只适用N•H替换了V•R,但却获得了明显的提高,它能提供比Phong更柔和、更平滑的高光,而且速度上也更快,因此成为很多CG软件中默认的光照渲染方法,同时也被集成到大多数的图形芯片中,而且在OpenGL和DirectX 3D的渲染管线中,它也是默认的光照模型。

     

    由于这两个光照模型公式基本相同,所以只解释一下N•H:

    N与前面相同,是顶点的单位法向量,而H则是入射光L和顶点到视点的单位向量的角平分线单位向量,通常也成为半角向量。其计算方法为:

    H = (L + V) / (|L + V|)

    Shader "Study/7_BlinnPhong" {
    	Properties{
    		_Color("Main Color", Color) = (1,1,1,1)
    		_SpecColor("Specular Color", Color) = (0.5, 0.5, 0.5, 1)// 高光颜色
    		_Shininess("Shininess", Range(0.01, 1)) = 0.078125// 高光指数【光泽度】
    		_MainTex("Base (RGB) Gloss (A)", 2D) = "white" {}
    	}
    		SubShader{
    		Tags{ "RenderType" = "Opaque" }
    		LOD 300
    
    		CGPROGRAM
    #pragma surface surf BlinnPhongTest
    		// 半角向量和BilnnPhong:使用入射光线和视线的中间平均值,即半角向量,然后使用该半角和法线计算出一个和视角相关的高光。
    		// 相关参数:【lightDir点到光源单位向量】【viewDir点到摄像机单位向量】【atten衰减系数】【_LightColor0场景中平行光的颜色】
    		float4 LightingBlinnPhongTest(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
    	{
    		// 1.半角向量:求(点到光源+点到摄像机)的单位向量,他们的中间平均值
    		float3 h = normalize(lightDir + viewDir);
    		// 2.漫反射系数【点到光源单位向量与法线向量的余弦值】
    		float diff = max(0, dot(s.Normal, lightDir));
    		// 3.高光底数【半角向量与法线向量的余弦值】
    		float nh = max(0, dot(s.Normal, h));
    		// 4.高光系数:根据高光低数和高光指数求得
    		float spec = pow(nh, s.Specular * 256) * s.Gloss;
    		float4 c;
    		// 5.最终光照rgb = 漫反射+半角高光
    		c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * _SpecColor.rgb * spec) * (atten * 2);
    		c.a = s.Alpha + _LightColor0.a * _SpecColor.a * spec * atten;
    		return c;
    	}
    	sampler2D _MainTex; // 主材质
    	fixed4 _Color;  // 主材质颜色
    	half _Shininess; // 高光指数
    	struct Input {
    		float2 uv_MainTex; // 主材质的UV信息
    	};
    	void surf(Input IN, inout SurfaceOutput o) {
    		// 取主纹理的对应当前像素点的值
    		fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
    
    		// Albedo反照率,即物体反射光的数量与外来光数量的比值。
    		// Albedo = 主纹理 x 主色调,反映了物体的基色,与任何光相关的信息(比如diffuse, shiness等)无关
    		o.Albedo = tex.rgb * _Color.rgb;    // 颜色纹理=主纹理*主材质颜色
    
    											// Gloss光滑度[0, 1],用于控制反射的模糊程度,值越大,高光反射越清晰,反之则越模糊,0将无高光效果。
    											// 光滑度的“滑”是面的概念,代表物体整体的光滑程度
    											// 比如说,同样一块金属,在它生锈的过程中,其反射就会慢慢变弱,可以通过Gloss值控制
    											// 实际上它是针对高光计算结果的附加系数
    		o.Gloss = tex.a;
    		o.Alpha = tex.a * _Color.a;      // 透明度
    										 // Shininess光泽度[0, 1],又叫高光指数或镜面反射指数,注意,它在SurfaceOutput结构中的命名(Specular)很容易让人误解为它是高光强度,其实不然,它是高光指数
    										 // 光泽度的“泽”是点的概念,代表物体某个高光点的光泽程度
    		o.Specular = _Shininess; // 越小光泽度越高,0为全白
    								 //o.Emission = tex.rgb;
    	}
    	ENDCG
    	}
    		Fallback "VertexLit"
    }


    展开全文
  • 高级光照-Blinn-Phong光照模型

    千次阅读 2018-08-19 00:19:36
    1. Blinn-Phong光照模型 1.1 实现代码 1.2 总结 1. Blinn-Phong光照模型 在基础光照教程中,简单的介绍了Phong光照模型(Phong lighting model),它给我们的场景带来基本的现实感。Phong模型看起来还不错...
  • Phong光照模型及OpenGL实现(一)

    千次阅读 2019-06-04 13:34:14
    phong模型 phong模型是一种经典的局部光照模型,支持点光源以及方向光源。phong模型将局部光照分为漫反射、镜面反射以及环境光三个部分。 漫反射 漫反射是指光线被粗糙表面无规则地向各个方向反射的现象,如图1 ...
  • 基础光照-Phong 光照模型

    万次阅读 2018-08-18 16:48:38
    1. Phong 光照模型 1.1 环境光照 Ambient Lighting 1.1.1 实现代码 1.2 漫反射光照 Diffuse Lighting 1.2.1 法向量 及实现代码 1.2.2 计算漫反射光照 1.2.3 法向量 注意事项 1.3 镜面光照 Specular Lighting 2...
  • 常见的三个光照模型 (1)漫反射与Lambert光照模型 粗糙的物体表面向各个方向等强度地反射光,这种等同度地散射现象称为光的漫反射(Diffuse Reflection)。产生光的漫反射现象的物体表面称为理想漫反射体,也称为...
  • 计算机图形学之Phong模型

    千次阅读 2019-02-28 19:52:01
    ## 新名词 软阴影 当多个点光源照射产生阴影重叠所呈现的模糊边界现象 反射光线计算方法 ## 差积公式 ##海伦公式
  • Phong光照模型

    2020-03-05 20:33:45
    基本概念   什么是光照模型?根据光学物理中的有关规律,计算出物体表面上任何一点投向观察者眼中的光的亮度大小和色彩组成的公式,从而在显示器上生成所显示的真实感图形。...Phong光照模型存在...
  •   上次说到Lambert漫反射光照模型,这是一个用来模拟粗糙表面对光线的漫反射现象的经验模型,对于纸张、粗糙墙壁等等来说,这个模型或许够用,但对于金属这样的光滑表面来说,我们就需要使用Phong模型来...
  • 目录Phong光照模型Phong光照模型的BRDF版本Blinn-Phong光照模型Blinn-Phong光照模型的BRDF版本两种模型的效果与优劣可能的误区参考资料 Phong光照模型 首先来看一下Phong光照模型的计算式: Lo(v⃗)=(cosθi∗cdiff+...
  • 【Unity Shader】 Blinn-phong光照模型

    千次阅读 2018-03-03 22:08:55
    Unity Shader Blinn-phong光照模型 一、镜面反射简介 对于许多物体,漫反射不够真实,比如擦亮的金属、光滑的塑料。要模拟光滑表面,还需要添加镜面高光反射的颜色,。在金属这样的光滑表现上,能模拟出光泽 ...
  • Unity Shader-Phong光照模型与Specular

    千次阅读 2016-12-09 13:34:52
    学完了兰伯特光照模型,再来学习一个更加高级一点的光照模型-Phong光照模型。兰伯特光照模型中主要就是漫反射,而实际上,光除了漫反射,还有镜面反射。一些金属类型的材质,往往表现出一种高光效果,用兰伯特模型是...
  • Phong光照模型及OpenGL实现(二)

    千次阅读 2019-06-25 06:48:53
    这一篇的主要内容是利用OpenGL实现phong模型,这里为了专注于phong模型,使用最为简单的正方体作为被光照影响的物体,并分别使用点平行光光源、光源以及聚光灯光源测试。 平行光光源 平行光光源一般用于表示极远处的...
  • 图形学基础 | Phong光照模型

    千次阅读 2019-05-02 15:39:47
    Phong模型是针对局部光照的经验模型。先过一遍原理,从wiki给出的2条公式入手: Ip=kaia+∑m∈lights(kd(L⇀m⋅N⇀)im,d+ks(R⇀m⋅V⇀)αim,s)I_p=k_ai_a+\sum_{m\in lights}\left(k_d\left({\overset\...
  • 摘抄“GPU Programming And Cg Language Primer 1rd Edition” 中文名“GPU编程与CG语言之阳春白雪下里巴人” Lambert模型较好地表现了粗糙表面上的光照现象,如石灰粉刷的墙壁、纸张等,但在用于诸如金属材质制...
  • Phong和Blinn-Phong光照模型 Phong和Blinn-Phong是计算镜面反射光的两种光照模型,两者仅仅有很小的不同之处。 1.Phong模型 在Phong光照模型中,关键是对发射向量的计算(如下图R向量) 上图中的位于表面...
  • Blinn-Phong 光照模型

    2020-02-19 23:46:11
    前面 我们使用Phong光照模型计算的高光反射,这里我们 用Blinn-Phong来实现高光反射计算。 在Blinn-Phong中,没有使用反射方向,而是引入了新的矢量h,它是通过对视角方向v 和光照方向 l 相加后再归一化得到的。 =...
  • Phong模型认为镜面反射的光强与反射光线和视线的夹角相关: 计算公式为:R + L = 2 * dot(N, L) * N 即 R = 2 * dot(N,L) * N - L 光源发出灯光,在材质表面反射,材质决定吸收灯光的什么分量和反射什么分量,而...
  • Phong和Blinn-Phong是计算镜面反射光的两种光照模型,两者仅仅有很小的不同之处。 1.Phong模型 Phone模型计算中的一个关键步骤就是反射向量R的计算: 上图中的位于表面“下面”的向量‘I’是原始‘I’向量...
  • OpenGL学习脚印:Blinn-Phong光照模型

    千次阅读 2016-10-29 16:01:56
    在前面基础光照部分,我们学习了Phong Shading模型,Blinn-Phong模型Phong模型的镜面光成分进行了改进,虽然在物理上解释没有Phong好,但是能更好地模拟光照。本节代码可以在我的github下载。 本节内容整理自: ...
  • 反射方向 fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormal)); reflect reflect是cg的函数,reflect(i,n) i为入射方向,n为法线方向,返回反射方向 ... worldLightDir = normalize...
  • 效果图 代码 Shader "DC/LightModel/BlinnPhong" { Properties { _MainTex ("Texture", 2D) = "white" {} //亮度调节 _SpecularK("SpecularK", Float) = 1 //控制发光区域大小,越小发光区域越大 ...
  • 效果图 ...Shader "DC/LightModel/Phong" { Properties { _MainTex ("Texture", 2D) = "white" {} //亮度调节 _SpecularK("SpecularK", Float) = 1 //控制发光区域大小,越小发光区域越大 ...
  • 前言:该系列教程主要参考自网站www.opengl-tutorial.org和learnopengl.com/,基于开源GUI框架imgui v1.61实现,imgui自带的例子里面直接集成了glfw+gl3w环境,本系列教程将gl3w换成了glew,glew具体环境配置可参考...
  • phong光照模型

    2020-09-04 16:50:20
    phong光照模型 经验模型 phong光照模型 = 环境光+漫反射+镜面高光 环境光: 光通常都不是来自于同一光源,而是来自散落于我们周围的很多光源,即使它们可能并不是那么显而易见。光的一个属性是,它可以向很多方向...
  • Phong光照模型由Lambert光照模型发展而来。Lambert描述了物体表面对漫反射的反应。Phong光照模型在此基础上增加了环境光和镜面反射两项内容。 Phong光照模型和Lambert光照模型一样,依然是个经验模型。这是说,Phong...
  • 比较好的一篇理解phong光照模型的文章 const VSHADER=` varying vec3 v_Normal; varying vec3 v_position; void main(){ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  • 最近比较精细的看计算机图形学知识,但是网上要么讲的太浅,要么内容太老。 精挑细选下,发现有一个比较好的视频课程: 对于初级入门者,可以从第一章看起来。 对于有一定基础的,可以跳着看。 ...

空空如也

1 2 3 4 5 ... 20
收藏数 4,433
精华内容 1,773
关键字:

phong