OpenGL中蒙板缓冲区的妙用

技术基础

Stenciling 蒙板
1、类似于深度缓冲的逐像素测试
2、与蒙板缓冲区内的值比较,测试失败的片断将被拒绝
3、当以下情况时,特定的操作将被执行:
 -蒙板测试失败
 -深度测试失败
 -深度测试通过
4、提供对象素更新非常细致的控制

OpenGL中相关API
glEnabe/glDisable(GL_STENCIL_TEST);
glStencilFunc(function,reference,mask);
glStencilOp(stencil_fail,depth_fail,depth_pass);
glStencilMask(mask);
glClear(...|GL_STENCIL_BUFFER_BIT);


对蒙板缓冲区的要求
如果使用蒙板,要求有足够多的位
实现应该支持从0到32位蒙板
8,4,1位是通常可用的
对于GLUT程序易用:
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH|GLUT_STENCIL);
glutCreateWindow("stencil example");


蒙板测试
测试就是拿参考值与象素的蒙板缓冲值比较
比较函数与深度测试一样:
NERVER, ALWAYS
LESS,LEQUAL
GREATER,GEQUAL
EQUAL,NOTEQUAL

位掩码控制比较过程
((ref & mask) op (svalue & mask))


蒙板操作
对蒙板的影响
-蒙板测试失败
-深度测试失败
-深度测试通过
可能的操作
-加,减(saturates)
-加,减(wrap, DX6 option)
-保持,替换
-清0,取反
蒙板缓冲值就是这样受控制的


蒙板缓冲区写掩码
位掩码控制蒙板值怎样回写进蒙板缓冲区
也用于清除
蒙板比较与写掩码允许把蒙板值当作多个子区域。


DX6 D3D API
与OpenGL完全相同的机能
SetRenderState控制更新:
-D3DRENDERSTATE_STENCILENABLE
-D3DRENDERSTATE_STENCILFUNC
-D3DRENDERSTATE_STENCILREF
-D3DRENDERSTATE_STENCILMASK
-D3DRENDERSTATE_STENCILWRITEMASK
-D3DRENDERSTATE_STENCILFAIL
-D3DRENDERSTATE_STENCILZFAIL
-D3DRENDERSTATE_STENCILPASS

DX6话题
记住查询可能的位!
加上建立并附加一个深度/蒙板界面(接口)

关于性能
今天的32位图形加速模式将24位深度加上8位蒙板封装在同一个内存字中。
如RIVA TNT
性能提示:如果使用了深度测试,蒙板测试是不会带来恶报的
不要认为蒙板测试是“昂贵的”,实际上如果已经进行了深度测试,蒙板是“免费的”。

平面反射:

绘制物体两次,第二次用glScalef(1,-1,-1)实现平面上的反射
第二次只能在反射面内画,超出部分要裁掉,这就要用到蒙板缓冲区。
1、蒙板清0。
2、将蒙板设为1,绘制反射平面多边形。
3、只在蒙板为1处绘制反射图象。
这个平面反射的思想可以递归使用。这需要更多的蒙板位。


平面阴影:建立阴影变换矩阵
原理
1、地平面的方程为ax+by+cz+d=0,光源在原点,空间一点(sx,sy,sz,1)的阴影:
过原点和空间一点的直线上的点坐标为(n*sx,n*sy,n*sz,1);
该直线与地平面交点为(m*sx,m*sy,m*sz,1),它应该满足地平面方程,所以
m(a*sx+b*sy+c*sz)+d=0; 则m=-d/(a*sx+b*sy+c*sz);
交点坐标{-d/(a*sx+b*sy+c*sz}(sx,sy,sz)
变换矩阵
-d, 0, 0, 0
0, -d, 0, 0
0, 0, 0, -d,
0, 0, 0, -d
2、光源在无限远,方向为D(dx,dy,dz),空间一点s(sx,sy,sz,1)的阴影:
这回直线为s+nD,与地面平面交点为:
a(sx+n.dx)+b(sy+n.dy)+c(sz+n.dz)+d=0 --- n=-(a.sz+b.sy+c.sz+d)/(dx+dy+dz)
交点为(sx,sy,sz)+n*(dx,dy,dz)
变换矩阵
-d, 0, 0, 0
0, -d, 0, 0
0, 0, -d, 0
0, 0, 0, -d
代码:
void shadowMatrix(GLfloat shadowMat[4][4], GLfloat groundplane[4], GLfloat lightpos[4])
{
GLfloat dot;
/*光源矢量与平面法向矢量的点积 */
dot = groundplane[X] * lightpos[X] +
groundplane[Y] * lightpos[Y] +
groundplane[Z] * lightpos[Z] +
groundplane[W] * lightpos[W];
shadowMat[0][0] = dot - lightpos[X] * groundplane[X];
shadowMat[1][0] = 0.f - lightpos[X] * groundplane[Y];
shadowMat[2][0] = 0.f - lightpos[X] * groundplane[Z];
shadowMat[3][0] = 0.f - lightpos[X] * groundplane[W];
shadowMat[X][1] = 0.f - lightpos[Y] * groundplane[X];
shadowMat[1][1] = dot - lightpos[Y] * groundplane[Y];
shadowMat[2][1] = 0.f - lightpos[Y] * groundplane[Z];
shadowMat[3][1] = 0.f - lightpos[Y] * groundplane[W];
shadowMat[X][2] = 0.f - lightpos[Z] * groundplane[X];
shadowMat[1][2] = 0.f - lightpos[Z] * groundplane[Y];
shadowMat[2][2] = dot - lightpos[Z] * groundplane[Z];
shadowMat[3][2] = 0.f - lightpos[Z] * groundplane[W];
shadowMat[X][3] = 0.f - lightpos[W] * groundplane[X];
shadowMat[1][3] = 0.f - lightpos[W] * groundplane[Y];
shadowMat[2][3] = 0.f - lightpos[W] * groundplane[Z];
shadowMat[3][3] = dot - lightpos[W] * groundplane[W];
}

基本的阴影绘制方法:
/* Render 50% black shadow color on top of whatever
the floor appearance is. */
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,
GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_LIGHTING); /* Force the 50% black. */
glColor4f(0.0, 0.0, 0.0, 0.5);
glPushMatrix();
/* Project the shadow. */
glMultMatrixf((GLfloat *) floorShadow);
drawDinosaur();
glPopMatrix();
并非如此容易1
显然上边的基本绘制代码没有使用蒙板。
这会导制加倍混合现象,使灰色阴影内部出现黑块,这是部分阴影象素被绘制了多次而产生的。
解决之道是使用蒙板记录已经绘制过的像素:
1、将蒙板清0
2、绘制蒙板为1的地平面
3、如果蒙板为1绘制阴影
4、如果阴影通过蒙板测试,将蒙板设为2
于是加倍混合消失。
并非如此容易2
即使消除了加倍混合,还会有其它问题。
深度缓冲Z值与物体阴影冲突,可能会在该画阴影的地方画上了地表面。
解决之道是把阴影的Z稍微升高一点。
光晕效果
1、蒙板缓冲清0;
2、绘制物体,物体所在部分的蒙板设为1。
3、用glScalef将物体放大
4、在蒙板为1以外的区域再次绘制物体
阴影体
即光源与物体边缘顶点连线形成的棱台体。
用蒙板标记像素是在阴影体之内还是之外。
两遍绘制:光照阴影体外的像素,无光照阴影体内的像素。
阴影体投影可能与其它物体的投影有部分相交,(注意:二维处理过程),
也就是说需要使用二维裁剪来区分阴影体内外的物体象素。
计算物体轮廓
我们需要找出物体轮廓,以便扩展出其阴影体。这个过程就象给它们画剪影。
三角形有最简单轮廓
复杂物体需要进行轮廓计算
-OpenGL的反馈机制和GLU1.2的拼嵌器(tesselator)可以帮助你。
动态阴影体很酷!
蒙板阴影算法
1、将场景的深度值绘制写入深度缓冲区
2、在蒙中板绘制阴影体时,如果深度测试通过则反转蒙板位
-glStencilOp(GL_KEEP,GL_KEEP,GL_INVERT);
3、阴影体外的像素取反偶数次
4、阴影体内的像素反转奇数次
反转举例[P35]
光源在上方,物体及其阴影体在前方,
视点与阴影体之间的点:反转0次,为1
阴影体内的点:反转1次,为1
阴影体外的点:反转2次,为0
绘制阴影内外
1、关闭光照计算绘制场景,只更新设有奇数模板位的象素
2、打开光照计算绘制场景,只更新设有偶数模板位的象素
3、阴影绘制完毕
多个物体与多个光源
进入与离开计数
场景中的区域可分成不同阴影级,不在任何阴影体内为0,
只在一个物体的阴影体内为1,在两物体阴影体相交区域内为2,
在三个物体阴影体相交区域内为3……
沿光线方向前进会出现进入阴影体和离开阴影体两个过程,我们可以以此来实现计数。
蒙板计数
1、对正对阴影体多边形使用GL_INCR蒙板操作
2、对背对阴影体多边形使用GL_DECR蒙板操作
3、绘制两遍
4、可以稍微扩展来自每个多边形的阴影体
5、注意观察裂缝
6、像素填充速率消耗巨大
抖动阴影体
多次绘制阴影体,次与次之间轻微抖动光源,累积缓冲区把每一次加起来。这样可以生成边缘柔和的软件阴影。
其它蒙板用法
1、数字溶解效果:蒙板缓冲区保存溶解掩模,使用掩模对两场景进行蒙板测试。
2、处理共面几何体,如贴标签纸。
共平面几何体会呈现z冲突,如绘制贴有标签纸的盒子时,可能出现标签纹理与盒子表面随机互相取代的现象,使用蒙板测试可以修正。
3、衡量深度复杂度
用蒙板计算像素更新的次数,并以此生成颜色码。这样就可以用不同的颜色绘制物体不同深度的部分。
4、实体几何建模
利用两个或多个相交几何体进行加、减、交运算构造新的复杂几何体称主实体几何。
绘制时需要特殊处理的是相交部分产生的切面,使用蒙板缓冲就可以得到这一信息。
结论
蒙板测试=提高视见品质,独特的效果
如今使用蒙板的游戏:
-Quake 3 :恐怖的体积阴影
-Unreal :邪恶的阴影效果

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/doing5552/archive/2008/04/22/2315438.aspx

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值