当前位置: 首页 > news >正文

无stencil buffer,绘制半透明planar shadow的一种方法

所谓planar shadow就是用一个投影矩阵(不是opengl流水线所谓之projection matrix,而是根据光源和投影面位置将一个点投射到投影面上的矩阵),将模型的所有顶点投射到投影面(比如地面)上。一般或者直接使用模型经过skin动画计算好的mesh进行投影并绘制,或者使用一个低模独立计算mesh然后投影绘制。无论哪种方法,都有一个问题,由于影子是很多三角形重叠在一起的,如果使用半透明材质绘制,众多的三角形会重叠在一起非常难看,且会有z-fighting现象闪烁不止。。当然如果有stencil buffer,可以使用stencil buffer进行mask,如果没有呢?确实在某些平台上没有stencil buffer,往往只能通过depth buffer做一些有限的模拟,本文所提的方法就是一例。

------------------------------------------

绘制过程:

1)首先绘制场景里所有其他东西

2)用0清除depth buffer:

glClearDepth(0);

glClear(GL_DEPTH_BUFFER_BIT);

3)确保深度测试开启,z write允许,并且设置depth func为always

glEnable(GL_DEPTH_TEST);

glDepthMask(GL_TRUE);

glDepthFunc(GL_ALWAYS);

4)禁止color buffer写入:

glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );

5)绘制影子(solid材质,无纹理,无blend)

经过上面的设置,实际上只会更新depth buffer, 写入z buffer时,默认的depth range是0~1,所以没有影子的地方,z buffer中是0,有影子的地方是一个大于0的值,因为影子一般不可能离near plane很近很近,所以这个值应该>0。

6)将model-view和projection矩阵设为单位阵

glMatrixMode (GL_MODELVIEW);

glLoadIdentity ();

glMatrixMode (GL_PROJECTION);

glLoadIdentity ();

7)重新开启color buffer写入,depth func为LEQUAL, 并关闭depth buffer的写入

8)绘制一个全屏的矩形,开启blend,设置矩形顶点颜色为半透明黑色

矩阵顶点坐标为:

glVertex3i (-1, -1, -0.99);
glVertex3i (1, -1, -0.99);
glVertex3i (1, 1, -0.99);
glVertex3i (-1, 1, -0.99);

绘制这个矩形时进行深度测试LEQUAL,z值-0.99是clip space中的值,通过depth range的映射,最终的z值是>0但很接近0的值,而影子应该是离near plane比这个面更远的(即z值比这个面的z值要大),所以经过LEQUAL的测试,画这个矩形的时候,z buffer上有影子的部分通过深度测试,而z buffer为0的地方不通过所以不绘制。而且由于启用了blend,矩形的像素还要和color buffer上的已有颜色进行混合,达到半透明效果。这样半透明的矩形被影子mask出来了,就得到了半透明的影子。

另外,为啥是-0.99而不是-1呢?因为clip space, -1就是在near clip plane上了,窗口坐标映射后的z值是0,0==0就能通过LEQUAL的测试了就会全部画出来了。。但是如果使用LESS测试,-1也就可以用了。

注:以上代码仅作参考,实际调用的都是引擎的接口

当然这个方法不完美,毛病也很多,比如影子会遮住人的脚,平台边缘无法裁掉影子等

ps:复习一下向z buffer写入z值的整个过程:此Z非x,y,z的z,而是eye space, clip space, NDC, windows space的z。通过modle-view变换,相机被变换到默认位置,即view为-z,此为eye space。我们在世界坐标系下,往往采用z up,所以此z非彼z,无论世界坐标z表示什么,到eye space之后,z值就表示深度了,所谓深度就是指离camera的距离。从eye space经过投影变换,到达clip space, clip space中(x,y,z)都被变换到[-w,w]的范围,再经过透视除法,x,y,z都除以w,使得x,y,z都在[-1,1]之间这就是NDC了(归一化坐标)。然后再经过viewport变换,变换到窗口坐标,此时z值会根据depth range进行映射,默认的是映射为[0,1]。clip,透视除法和视口变换都属于图元装配过程,之后是光栅化阶段,图元被光栅化为片段(fragment),在光栅化阶段,为了降低zfighting现象,可以使用polygon offset, polygon offset是在进行深度测试和写入zbuffer之前,将计算好的z值加上一个偏移量,如果深度测试通过,会将原始深度值(而不是加上offset之后的z值)写入zbuffer。也就是说无论是否使用polygon offset,最终写入zbuffer的z值是视口变换之后通过depth range映射的windows coordinate z值,但polygon offset会对depth test产生影响。

相关文章:

  • 2020牛客暑期多校第十场 A - Permutation(思维)
  • 2020牛客暑期多校第十场 E - Game(思维)
  • 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之30---基于BREW的PTT服务...
  • HDU - 6805 Deliver the Cake(拆点+最短路)
  • 《BREW进阶与精通——3G移动增值业务的运营、定制与开发》连载之31---LBS基于BREW的位置服务...
  • STL之multiset
  • Java网络编程从入门到精通(16):客户端套接字(Socket)的超时
  • 2020牛客暑期多校第十场 C - Decrement on the Tree(树的思维好题)
  • 页面校验用通用js
  • SPOJ - FIBOSUM Fibonacci Sum(递推公式/矩阵快速幂)
  • 保证唯一性只能靠建唯一索引
  • HDU - 6860 Fluctuation Limit(双向贪心/思维)
  • 付出就有回报,坚持才会胜利
  • 2020牛客暑期多校第九场 E - Groundhog Chasing Death(gcd+质因数分解)
  • 高中毕业从事研发,我应该继续提高学历吗?——网上答疑(33)
  • ES6指北【2】—— 箭头函数
  • 自己简单写的 事件订阅机制
  • [译]如何构建服务器端web组件,为何要构建?
  • Angular6错误 Service: No provider for Renderer2
  • css布局,左右固定中间自适应实现
  • extract-text-webpack-plugin用法
  • gf框架之分页模块(五) - 自定义分页
  • github从入门到放弃(1)
  • nodejs调试方法
  • UMLCHINA 首席专家潘加宇鼎力推荐
  • 从零开始的无人驾驶 1
  • 当SetTimeout遇到了字符串
  • 多线程事务回滚
  • 给初学者:JavaScript 中数组操作注意点
  • 计算机常识 - 收藏集 - 掘金
  • 你不可错过的前端面试题(一)
  • 少走弯路,给Java 1~5 年程序员的建议
  • 最近的计划
  • kubernetes资源对象--ingress
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • #Ubuntu(修改root信息)
  • (1)(1.13) SiK无线电高级配置(六)
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (4)事件处理——(2)在页面加载的时候执行任务(Performing tasks on page load)...
  • (bean配置类的注解开发)学习Spring的第十三天
  • (day 12)JavaScript学习笔记(数组3)
  • (八)c52学习之旅-中断实验
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (顶刊)一个基于分类代理模型的超多目标优化算法
  • (算法)N皇后问题
  • (未解决)macOS matplotlib 中文是方框
  • (一)基于IDEA的JAVA基础12
  • (转)c++ std::pair 与 std::make
  • (转)微软牛津计划介绍——屌爆了的自然数据处理解决方案(人脸/语音识别,计算机视觉与语言理解)...
  • (转载)(官方)UE4--图像编程----着色器开发
  • (转载)虚函数剖析
  • .net framework profiles /.net framework 配置
  • .net遍历html中全部的中文,ASP.NET中遍历页面的所有button控件
  • .NET值类型变量“活”在哪?
  • /bin/bash^M: bad interpreter: No such file ordirectory