8.WebGL Shader光照

概述

Phong模型是一种用于计算3D物体表面的光照效果的经典算法,它得名于其发明者Bui Tuong Phong(https://en.wikipedia.org/wiki/Bui_Tuong_Phong)。它主要由三个成分构成:环境光、漫反射和镜面反射。
在这里插入图片描述
1.环境光:表示整个场景中的均匀光照,不会受到物体表面属性的影响。
2.漫反射:表示入射光线与物体表面之间的散射,使物体表面上的每个点都能接收到来自光源的光线。其中平行光与点光源和漫反射有直接关系。
3.镜面反射:表示入射光线经过反射后集中在一个方向,形成高光的效果,模拟光线在镜面上反射的现象。
Phong模型通过将这三种光照成分相加得到最终的颜色值,并且可以根据物体表面的材质属性(如反射率、粗糙度等)调整每种光照成分的权重,从而实现更真实的光照效果。以下是phong模型的计算公式:
在这里插入图片描述

光照类型

在这里插入图片描述

法线

在这里插入图片描述

平行光

在这里插入图片描述
在这里插入图片描述

点光源

在这里插入图片描述

在这里插入图片描述

环境光

在这里插入图片描述

镜面反射/高光

Phong模型我们不再过多的解释,今天我们只讲解其中一小部分,“镜面反射”。下面是“镜面反射”的原理图。

高光.png

在现实生活中,金属和抛光表面等材料具有镜面反射,根据相机角度或观察者面对物体的位置,它们看起来会更亮。因此,我们的目的是求的相机视角和反射光线的夹角,想象一下,如果我们在太阳光下看一个镜子,镜子里反射回来很亮很亮的一个光斑。这就是其中一个高光的现实例子。

高光现实.png

基础知识

基础知识我们不过多去赘述,大家回忆一下向量,向量加法,向量的点乘以及向量的模等概念,如果不明白的可以翻翻以前的文章或者自行百度一下。

向量投影

向量投影.png

向量投影我们需要复习下,上图表达的是向量l在向量n上的投影向量,那么用数学公式表示如下所示:
P ( l ) = n ^ ∗ f P(l)=\hat{n}∗f P(l)=n^f

其中 f 是一个标量,是 l 在 n 上的投影长度,向量 n ^ 是向量 n 的单位向量。 其中f是一个标量,是l在n上的投影长度,向量\hat{n}是向量n的单位向量。 其中f是一个标量,是ln上的投影长度,向量n^是向量n的单位向量。

现在我们来求f的值:
f = ∣ l ∣ c o s ( θ ) f = |l|cos(θ) f=lcos(θ)
那么得:
P ( l ) = n ^ ∗ ∣ l ∣ c o s ( θ ) P(l)=\hat{n}∗ |l|cos(θ) P(l)=n^lcos(θ)
再之后我们根据点乘公式可以得到:
l ⋅ n = ∣ l ∣ ∣ n ∣ c o s ( θ ) l·n = |l||n|cos(θ) ln=l∣∣ncos(θ)

c o s ( θ ) = ( l ⋅ n ) / ∣ l ∣ ∣ n ∣ cos(θ) =(l·n)/|l||n| cos(θ)=(ln)/∣l∣∣n

进而我们得到:
P ( l ) = n ^ ∗ ∣ l ∣ ( ( l ⋅ n ) / ∣ l ∣ ∣ n ∣ ) = n ^ ∗ ( l ⋅ n ) / ∣ n ∣ P(l) = \hat{n} * |l|((l·n)/|l||n|) = \hat{n} * (l·n)/|n| P(l)=n^l((ln)/∣l∣∣n)=n^(ln)/∣n
再变形:
P ( l ) = n / ∣ n ∣ ∗ ( l ⋅ n ) / ∣ n ∣ = n ∗ ( l ⋅ n ) / ∣ n ∣ 2 = n ∗ ( l ⋅ n ) / ( n ⋅ n ) P(l) = n/|n| * (l·n)/|n|=n * (l·n)/|n|²=n * (l·n)/(n·n) P(l)=n/∣n(ln)/∣n=n(ln)/∣n2=n(ln)/(nn)

反射向量

下图是反射的向量模型,我们最终目的是要求R向量。我们已知的参数值是向量L和向量N。

计算过程高光.png

我们由上面向量L在向量N的投影等于:
P ( l ) = n ∗ ( l ⋅ n ) / ( n ⋅ n ) P(l) = n * (l·n)/(n·n) P(l)=n(ln)/(nn)
那么我们可以求的图上黄色细线的向量为:
Q = 2 ( L − P ( l ) ) Q= 2(L-P(l)) Q=2(LP(l))
那么向量R等于
L − R = Q L-R = Q LR=Q
所以:
R = L − Q = L − 2 ( L − P ( l ) ) = 2 P ( l ) − L = 2 ∗ n ∗ ( l ⋅ n ) / ( n ⋅ n ) − l R = L-Q = L-2(L-P(l))= 2P(l)-L =2*n * (l·n)/(n·n)-l R=LQ=L2(LP(l))=2P(l)L=2n(ln)/(nn)l

最后,如果 n 首先是一个单位向量(长度为 1 的向量),然后 ∣ n ∣ = 1 ,还有就是向量 n ^ = n 。 最后,如果n首先是一个单位向量(长度为 1 的向量),然后|n|=1,还有就是向量\hat{n}=n。 最后,如果n首先是一个单位向量(长度为1的向量),然后n=1,还有就是向量n^=n

所以最终公式变形为:
R = 2 ∗ n ∗ ( l ⋅ n ) − l R =2*n * (l·n)-l R=2n(ln)l

应用

上述推导出来的公式实际上在glsl已经给我们封装成一个函数名字叫做:reflect,我再glsl中级课程中讲到了此函数的应用。

求出反射光线来我们需要和相机的向量求的二者之间的夹角,就像上面我们提到,如果相机方向和反射方向正好在一条直线上,那么此时肯定是最了“刺眼”的,所以我需要又要用到点乘然后判断二者之间的夹角。代码一版如下所示:

 float dotRV = clamp(dot(reflect(lightDirection, normal), -eyedirection), 0., 1.);

一般如果你觉得高亮对比度还不够高,我们可以用pow函数去加大对比度。

 vec3 specular =  pow(dotRV, 50.) ;

阴影

我们实现阴影效果使用的是叫阴影映射的技术, 而实现阴影映射需要用到帧缓冲区。默认情况下,WebGL 在颜色缓冲区绘图,使用隐藏面消除的话,还会用到深度缓冲区。即正常绘制的情况下包含:

  • 颜色缓冲区
  • 深度缓冲区

帧缓冲区对象 framebuffer object可以用来代替颜色缓冲区或深度缓冲区。绘制在帧缓冲区中的对象并不会直接显示canvas上,可以先对帧缓冲区中的内容进行一些处理再显示,或者直接用其中的内容作为纹理图像。在帧缓冲区中进行绘制的过程又称为离屏绘制 offscreen drawing。
绘制操作并不是直接发生在帧缓冲区中,而是发生在帧缓冲区所关联的对象 attachment上,一个帧缓冲区有3个关联对象:

颜色关联对象 color attachment,对应颜色缓冲区
深度关联对象 depth attachment,对应深度缓冲区
模板关联对象 stencil attachment,对应模板缓冲区。
而我们现在先有这个概念,来看看帧缓冲区的创建和配置:

  • 创建帧缓冲区对象 gl.createFramebffer().
  • 创建文理对象并设置其尺寸和参数gl.createTexture()、gl.bindTexture()、gl.texImage2D()、gl.Parameteri().
  • 创建渲染缓冲区对象 gl.createRenderbuffer().
  • 绑定渲染缓冲区对象并设置其尺寸gl.bindRenderBuffer()、gl.renderbufferStorage().
  • 将帧缓冲区的颜色关联对象指定为一个文理对象gl.frambufferTexture2D().
  • 将帧缓冲区的深度关联对象指定为一个渲染缓冲区对象gl.framebufferRenderbuffer().
  • 检查帧缓冲区是否正确配置gl.checkFramebufferStatus().
  • 在帧缓冲区中进行绘制 gl.bindFramebuffer().

阴影映射的原理很简单,首先从光的角度渲染场景,从光的角度看到的所有东西都被点亮了,而看不见的部分一定是在阴影里.。想象有一个盒子和它的光源照射下的地板,由于光源会看到这个盒子而它后面的地板部分是看不到的.那么当视线角度变化的时候,从光源角度照不到的那部分地板就渲染为阴影,原理如下图
在这里插入图片描述

接着我们使用阴影映射的算法实现, 它要使用到前面介绍的帧缓冲区. 阴影映射要渲染两遍:

从光源的角度渲染场景,同时把场景的深度值当成纹理渲染到帧缓冲区,也就是把它当作数据容器.
从眼睛的角度渲染场景,把物体真正渲染到画布中,同时对比纹理的深度值,将阴影部分也渲染出来.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值