WebGL光照阴影映射

  原文地址:WebGL光照阴影映射
  经过前面的学习,webgl的基本功能都已经掌握了,我们不仅掌握了着色器的编写,图形的绘制,矩阵的变换,添加光照,还通过对webgl的基础api封装,编写出了便利的工具库. 是时候进一步深入学习webgl的高级功能了,我认为要做逼真的3D特效,阴影绝对是一个必不可少的环节.现在我们就在之前光照的基础上添加阴影效果吧.
  首先看一下阴影效果的实例:
阴影综合(多物体高精度)
点光源聚光灯阴影

内容大纲

   我们以阴影综合(多物体高精度PCF)为例, 开始学习阴影相关知识.

帧缓冲

阴影映射(shadow mapping)

提高阴影精度

抗锯齿(PCF)

帧缓冲

  我们实现阴影效果使用的是叫阴影映射的技术, 在此之前要提到以下两个对象:帧缓冲区对象和渲染缓冲区对象.
  帧缓冲区对象(framebuffer object)可以用来代替颜色缓冲区或深度缓冲区.绘制在帧缓冲区中的对象并不会直接显示canvas上,可以先对帧缓冲区中的内容进行一些处理再显示,或者直接用其中的内容作为纹理图像.在帧缓冲区中进行绘制的过程又称为离屏绘制(offscreen drawing).
  而渲染缓冲区对象就是我们绘图使用的缓冲区, 它会直接把内容渲染到canvas中.
  而我们现在先有这个概念,来看看帧缓冲区的创建和配置:
1. 创建帧缓冲区对象 gl.createFramebffer().
2. 创建文理对象并设置其尺寸和参数 gl.createTexture()、gl.bindTexture()、gl.texImage2D()、gl.Parameteri().
3. 创建渲染缓冲区对象 gl.createRenderbuffer().
4. 绑定渲染缓冲区对象并设置其尺寸 gl.bindRenderBuffer()、gl.renderbufferStorage().
5. 将帧缓冲区的颜色关联对象指定为一个文理对象 gl.frambufferTexture2D().
6. 将帧缓冲区的深度关联对象指定为一个渲染缓冲区对象 gl.framebufferRenderbuffer().
7. 检查帧缓冲区是否正确配置 gl.checkFramebufferStatus().
8. 在帧缓冲区中进行绘制 gl.bindFramebuffer().

  它的创建和配置是一个非常繁琐的过程,我们先熟悉了怎么使用,再慢慢研究它内部的原理,所以先把上面的步骤封装成一个黑盒子,我这里就是initFramebufferObject这个函数.

阴影映射(shadow mapping)

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

  接着我们使用阴影映射的算法实现, 它要使用到前面介绍的帧缓冲区. 阴影映射要渲染两遍:
第一遍是从光源的角度渲染场景,同时把场景的深度值当成纹理渲染到帧缓冲区,也就是把它当作数据容器.
第二遍是从眼睛的角度渲染场景,把物体真正渲染到画布中,同时对比纹理的深度值,将阴影部分也渲染出来.

  左边的图像是第一遍渲染的原理, 一个方向光源(所有的光线都是平行的)在立方体下面的表面投下阴影.我们通过用光源的视图投影矩阵渲染场景(从光线的角度)来创建景深图然后把它存储到帧缓冲区中.
  右边的图形是第二遍渲染的原理, 从眼睛的视图投影矩阵渲染场景(从眼睛的角度), 光源角度下的xy坐标相同的c点和p点,p深度值比c要大, 那么它一定处于阴影当中,那么p点就渲染为阴影.

WebGL光照阴影映射


  来看实现以上功能的着色器代码,因为要渲染两遍,所以也就要建立两对的着色器(顶点/片段),顶点着色器比较简单,基本不涉及阴影映射,在此省略:

阴影片段着色器

#ifdef GL_ES precision mediump float; #endif void main() { gl_FragColor = vec4(gl_FragCoord.z, 0.0, 0.0, 0.0); //将深度值z存放到第一个分量r中 }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wspggd.html