图中有两条光线,一条沿视角方向,另一条偏右
采用阴影体算法,实现了多光源,多物体的阴影。
这个算法没有采用顶点shader,所以速度比较慢,学习算法用,在实际工程中用效率就太低了。。。
算法还有个缺陷,只对凸多面体有效,如果物体是凹多面体阴影就会错误。因为我不知道如何确定凹多面体的轮廓,从而构造一个封闭的阴影体。非常希望有牛人能指点一二!
主要代码如下
计算轮廓
/* 计算轮廓(silhouette edge),采用的算法,伪代码如下
for 三角形tri in 所有三角形
如果tri是前向面
for 边e1 in 三角形tri的三条边
如果e1在轮廓列表中,从轮廓列表中移除e1
否则把e1加入轮廓列表中
默认物体是凸多面体。。。
对凹多面体,如何用算出来的轮廓构造阴影体,至今还不知道,希望有前辈能提点提点。
*/
void ShadowVolume::Build(const Model &model, const Vector3 &lightPos) {
m_lightPos = Vector3(lightPos);
m_silhouetteEdges.clear();
m_silhouettePoints.clear();
for(int j = 0; j < model.m_indexNum; j += 3) {
Vector3 v1 = model.m_vbuf[model.m_ibuf[j]];
Vector3 v2 = model.m_vbuf[model.m_ibuf[j + 1]];
Vector3 v3 = model.m_vbuf[model.m_ibuf[j + 2]];
Vector3 n = (v2 - v1).crossProduct(v3 - v1);
n.normalise();
if(n.dotProduct(lightPos - v1) > EPSILON) { // 注意此处要减去v1,才是正确的可见面判别。否则在离物体很近的时候会出现误差
AddEdge(SilEdge(v1, v2));
AddEdge(SilEdge(v2, v3));
AddEdge(SilEdge(v3, v1));
}
}
list<SilEdge>::iterator it = m_silhouetteEdges.begin();
m_silhouettePoints.push_back((*it).edges[0]);
m_silhouettePoints.push_back((*it).edges[1]);
m_silhouetteEdges.erase(it);
bool erased = true;
// 把轮廓边缘的点提取出来,构造出多边形的点集合。下面的方法很糟糕,可是我想不到更好的办法了。。
while(m_silhouetteEdges.size() != 0 && erased) {
erased = false;
for(it = m_silhouetteEdges.begin();it != m_silhouetteEdges.end(); it++) {
SilEdge &edge = *it;
if(edge.edges[0] == m_silhouettePoints[m_silhouettePoints.size() - 1]) {
if(edge.edges[1] != m_silhouettePoints[0]) {
m_silhouettePoints.push_back(edge.edges[1]);
}
m_silhouetteEdges.erase(it);
erased = true;
break;
}
if(edge.edges[1] == m_silhouettePoints[m_silhouettePoints.size() - 1]) {
if(edge.edges[0] != m_silhouettePoints[0]) {
m_silhouettePoints.push_back(edge.edges[0]);
}
m_silhouetteEdges.erase(it);
erased = true;
break;
}
if(edge.edges[1] == m_silhouettePoints[0] || edge.edges[0] == m_silhouettePoints[0]) {
m_silhouetteEdges.erase(it);
erased = true;
break;
}
}
}
}
绘制阴影体
void DrawVolume() {
m_infinitePoints.clear();
for(size_t i = 0; i < m_silhouettePoints.size(); i++) {
Vector3 a = m_silhouettePoints[i] - m_lightPos;
Vector3 infinite = a;
infinite.normalise();
infinite *= 100;
infinite += a;
m_infinitePoints.push_back(infinite);
}
glBegin(GL_POLYGON);
for(int i = m_infinitePoints.size() - 1; i >= 0; i--) {
glVertex3f(m_infinitePoints[i].x, m_infinitePoints[i].y, m_infinitePoints[i].z);
}
glEnd();
glBegin(GL_POLYGON);
for(size_t i = 0; i < m_silhouettePoints.size(); i++) {
glVertex3f(m_silhouettePoints[i].x, m_silhouettePoints[i].y, m_silhouettePoints[i].z);
}
glEnd();
glBegin(GL_QUADS);
int n = m_silhouettePoints.size();
for(int i = 0; i < n; i++) {
glVertex3f(m_silhouettePoints[(i+1) % n].x, m_silhouettePoints[(i+1) % n].y, m_silhouettePoints[(i+1) % n].z);
glVertex3f(m_silhouettePoints[i].x, m_silhouettePoints[i].y, m_silhouettePoints[i].z);
glVertex3f(m_infinitePoints[i].x, m_infinitePoints[i].y, m_infinitePoints[i].z);
glVertex3f(m_infinitePoints[(i+1) % n].x, m_infinitePoints[(i+1) % n].y, m_infinitePoints[(i+1) % n].z);
}
glEnd();
}
用蒙版缓存绘制阴影
// 用蒙版缓存(stencil buffer)的方法绘制阴影。
void Render() {
// 绘制没有光线时的场景
for(size_t k = 0; k < mLight.size(); k++) {
glDisable(GL_LIGHT0 + k);
}
glBlendFunc(GL_ONE, GL_ONE);
drawWall();
for(size_t i = 0; i < mObjects.size(); i++) {
mObjects[i]->Render();
}
//处理每条光线
for(size_t k = 0; k < mLight.size(); k++) {
glClear(GL_STENCIL_BUFFER_BIT);
// 计算每一个物体阴影体
for(size_t i = 0; i < mObjects.size(); i++) {
mObjects[i]->BuildShadow(mLight[k]->position);
}
glDepthFunc(GL_LESS);
// 绘制阴影体,采用卡马克的depth fail算法
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthMask(GL_FALSE);
glEnable(GL_CULL_FACE);
glEnable(GL_STENCIL_TEST);
glEnable(GL_POLYGON_OFFSET_FILL);
// 对重叠表面的处理
glPolygonOffset(0.0f, 100.0f);
glCullFace(GL_FRONT);
glStencilFunc(GL_ALWAYS, 0x0, 0xff);
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
for(size_t i = 0; i < mObjects.size(); i++) {
mObjects[i]->RenderShadow();
}
glCullFace(GL_BACK);
glStencilFunc(GL_ALWAYS, 0x0, 0xff);
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
for(size_t i = 0; i < mObjects.size(); i++) {
mObjects[i]->RenderShadow();
}
glDepthFunc(GL_LEQUAL);
glDisable(GL_CULL_FACE);
glDisable(GL_POLYGON_OFFSET_FILL);
// 绘制整个场景,如果蒙版缓存的值为1,说明有阴影,不绘制
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_TRUE);
glStencilFunc(GL_EQUAL, 0x0, 0xff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// 开启当前光线的光照效果
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_LIGHT0 + k);
for(size_t i = 0; i < mObjects.size(); i++) {
mObjects[i]->Render();
}
drawWall();
glDisable(GL_LIGHT0 + k);
glDisable(GL_BLEND);
}
}