Как реализовать алгоритм Z-Fail в OpenGL?
Я пишу код, как сказал мне Урок 27 NeHe, но это алгоритм z-pass. Когда я в тени, тень исчезла. Кто-то сказал мне, что я могу использовать алгоритм z-fail для решения этой проблемы. поэтому я провожу два дня, чтобы исследовать алгоритм z-fail. Наконец, я не могу понять это. Моя программа никогда не запускается так, как я думаю.
Алгоритм z-fail, как указано в вики:
Ошибка глубины Примерно в 2000 году несколько человек обнаружили, что метод Хайдмана может работать для всех положений камеры путем изменения глубины. Вместо подсчета теневых поверхностей перед поверхностью объекта, поверхности за ним могут быть подсчитаны так же легко, с тем же конечным результатом. Это решает проблему нахождения глаза в тени, поскольку теневые объемы между глазом и объектом не учитываются, но вводит условие, что задний конец теневого объема должен быть ограничен, иначе тени будут отсутствовать там, где указывает объем назад в бесконечность.
Отключить запись в буферы глубины и цвета.
Используйте лицевую отбраковку.
Установите операцию трафарета на приращение глубины (только подсчет теней за объектом).
Визуализируйте теневые объемы.
Используйте отбраковку спины.
Установите операцию трафарета на уменьшение при ошибке глубины.
Визуализируйте теневые объемы.
Главный вопрос, я думаю, это проверка глубины. На шагах 3 и 6 трафарет основан на сбое глубины. Хотя он может показать тень, но он может быть затенен на объекте до него (то есть на объекте, значение буфера глубины которого меньше его). эффект тени выглядит беспорядок.
Но в алгоритме z-pass операция трафарета основана на проходе глубины, это означает, что он не только может показать тень, но и затенять только объект позади него, что соответствует системе глаз.
так как решить эту проблему, чтобы мой алгоритм сбоя глубины показал тень на нужных объектах.
вот мой код алгоритма z-fail (где-то может быть, где, пожалуйста, помогите мне узнать, эффект тени ужасен)
VECTOR vec;
void shadowvolume(SECTOR &sec,float *lp)
{
unsigned int p1, p2;
VECTOR v1, v2;
int i, j, k, jj;
for (i=0; i<sec.numplanes;i++)
{
if (sec.planes[i].visible)
{
for (j=0;j<3;j++)
{
k = sec.planes[i].neigh[j];
if ((!k) || (!sec.planes[k-1].visible))//如果以第k个点开始的邻边没有相邻平面或者相邻平面不可见
{
// here we have an edge, we must draw a polygon
p1 = sec.planes[i].p[j]-1;//邻边的起点
jj = (j+1)%3;
p2 = sec.planes[i].p[jj]-1;//邻边的终点
//calculate the length of the vector
v1.x = (sec.points[p1].vec.x - lp[0])*100;
v1.y = (sec.points[p1].vec.y - lp[1])*100;
v1.z = (sec.points[p1].vec.z - lp[2])*100;
v2.x = (sec.points[p2].vec.x - lp[0])*100;
v2.y = (sec.points[p2].vec.y - lp[1])*100;
v2.z = (sec.points[p2].vec.z - lp[2])*100;
glBegin(GL_TRIANGLE_STRIP);//将光源连到邻边的起点并延长,将光源连到邻边的终点的并延长,最后延长出来的梯形,画了过后模板缓冲区的值加1
glVertex3f(sec.points[p1].vec.x,sec.points[p1].vec.y,sec.points[p1].vec.z);
glVertex3f(sec.points[p1].vec.x + v1.x,sec.points[p1].vec.y + v1.y,sec.points[p1].vec.z + v1.z);
glVertex3f(sec.points[p2].vec.x,sec.points[p2].vec.y,sec.points[p2].vec.z);
glVertex3f(sec.points[p2].vec.x + v2.x,sec.points[p2].vec.y + v2.y,sec.points[p2].vec.z + v2.z);
glEnd();
}
}
// caps
glBegin(GL_TRIANGLES);
for(k=0;k<3;k++)
glVertex3fv((float*)&sec.points[sec.planes[i].p[k]-1].vec);
glEnd();
glBegin(GL_TRIANGLES);
for(k=2;k>=0;k--)
{
vec.x=sec.points[sec.planes[i].p[k]-1].vec.x+(sec.points[sec.planes[i].p[k]-1].vec.x-lp[0])*100;
vec.y=sec.points[sec.planes[i].p[k]-1].vec.y+(sec.points[sec.planes[i].p[k]-1].vec.y-lp[1])*100;
vec.z=sec.points[sec.planes[i].p[k]-1].vec.z+(sec.points[sec.planes[i].p[k]-1].vec.z-lp[2])*100;
glVertex3fv((float*)&vec);
}
glEnd();
}
}
}
void CastShadow(SECTOR &sec, float *lp)
{//lp是光源相对于物体的位置
float side;
glEnable(GL_CULL_FACE);
int i;
for (i=0;i<sec.numplanes;i++)
{
side =sec.planes[i].planeeq.a*lp[0]+sec.planes[i].planeeq.b*lp[1]+sec.planes[i].planeeq.c*lp[2]+sec.planes[i].planeeq.d*lp[3];
if (side>0)
sec.planes[i].visible = TRUE;
else
sec.planes[i].visible = FALSE;
}
glDisable(GL_LIGHTING);
glDepthMask(GL_FALSE);
glDepthFunc(GL_LEQUAL);
glEnable(GL_STENCIL_TEST);
glColorMask(0, 0, 0, 0);
glStencilFunc(GL_ALWAYS, 0, 0xffffffff);
glCullFace(GL_FRONT);
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
//glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
shadowvolume(sec,lp);
glCullFace(GL_BACK);
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
//glStencilOp(GL_KEEP,GL_KEEP, GL_INCR);
shadowvolume(sec,lp);
glColorMask(1, 1, 1, 1);
//draw a shadowing rectangle covering the entire screen
glColor4f(0.0f, 0.0f, 0.0f,0.4f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glStencilFunc(GL_NOTEQUAL, 0, 0xffffffff);
//glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glPushMatrix();
glLoadIdentity();
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(-0.1f, 0.1f,-0.0010f);
glVertex3f(-0.1f,-0.1f,-0.0010f);
glVertex3f( 0.1f, 0.1f,-0.0010f);
glVertex3f( 0.1f,-0.1f,-0.0010f);
glEnd();
glPopMatrix();
glDisable(GL_BLEND);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
glEnable(GL_LIGHTING);
glDisable(GL_STENCIL_TEST);
glShadeModel(GL_SMOOTH);
glDisable(GL_CULL_FACE);
}
класс VECTOR выглядит так:
class VECTOR
{
public:
float x,y,z;
bool operator==(VECTOR vec)
{
if(x==vec.x && y==vec.y && z==vec.z)
return true;
return false;
}
};
класс SECTOR и другие такие:
class PLANEEQ
{
public:
float a,b,c,d;
};
class PLANE
{
public:
unsigned int p[3];//点的序号
VECTOR normal[3];
unsigned int neigh[3];//平面3个相依平面的序号
PLANEEQ planeeq;
bool visible;
PLANE()
{
neigh[0]=0;
neigh[1]=0;
neigh[2]=0;
planeeq.a=0;
planeeq.b=0;
planeeq.c=0;
planeeq.d=0;
visible=false;
}
};
class SECTOR
{
public:
int numpoints;
int numplanes;
vector<VERTEX> points;
vector<PLANE> planes;
MATERIAL material;
bool read();
bool loadtexture();
bool build();
bool plane_calc();
void SetConnectivity();
SECTOR& SECTOR::subdivide(long depth);
SECTOR(string str1,string str2):modelfilename(str1),texturefilename(str2)
{
numpoints=0;
numplanes=0;
}
SECTOR()
{
numpoints=0;
numplanes=0;
}
private:
FILE *modelfilein,*texturefilein;
string modelfilename,texturefilename;
char oneline[255];
UINT texturename;
AUX_RGBImageRec *TextureImage;
};
class POSITION
{
public:
float x,y,z,w;
};
функция DrawGLScene в моем main.cpp выглядит следующим образом:
int DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |GL_STENCIL_BUFFER_BIT);
glLoadIdentity();
DrawGLRoom();
glLoadIdentity();
GLfloat xtrans = -xpos;
GLfloat ztrans = -zpos;
GLfloat ytrans = -ypos-1.2f;
GLfloat sceneroty = 360.0f - yrot;
glRotatef(lookupdown,1.0f,0,0);
glRotatef(sceneroty,0,1.0f,0);
glTranslatef(xtrans, ytrans, ztrans);
brick_sec.build();
floor_sec.build();
//wall_sec.build();
//CastShadow(wall_sec,(float *)&lightgroup.lights[0].pos);
CastShadow(brick_sec,(float*)&lightgroup.lights[0].pos);
CastShadow(floor_sec,(float*)&lightgroup.lights[0].pos);
lightgroup.build();
glColor4f(0.7f, 0.4f, 0.0f, 1.0f);
glDisable(GL_LIGHTING);
glDepthMask(GL_FALSE);
glTranslatef(lightgroup.lights[0].pos.x, lightgroup.lights[0].pos.y, lightgroup.lights[0].pos.z);
gluSphere(q, 0.2f, 16, 8);
glEnable(GL_LIGHTING);
glDepthMask(GL_TRUE);
if(space_time>0)
{
ypos=sin(space_time*3.1415926/180);
space_time-=4;
}
else
{
sp=false;
}
//glFlush();
return TRUE; // Everything Went OK
}
Поскольку моя репутация меньше 10, я не могу запечатлеть эффект тени, чтобы показать вам, как это плохо выглядит! Пожалуйста, помогите мне, я бы спасибо за ваше внимание и ваше время!
спасибо Надзеро за 5 репутацию, теперь я могу сделать снимок экрана, чтобы показать эффект. Я добавлю подробное описание ниже.
эффект алгоритма z-pass: когда я не в эффекте, все в порядке!(оранжевый горшок представляет свет)
но когда я нахожусь в wall_shadow, это не нормально! wall_shadow ушел, хотя brick_shadow все еще там.
поэтому мне нужен алгоритм z-fail для решения этой проблемы. Но последний эффект, который реализовал мой код, выглядит так:галочка обозначает, что эффект тени правильный, крестик обозначает, что тень не должна появляться на объекте.
еще один скриншот,
1 ответ
Ага, наконец-то я нахожу проблему в своем коде. Я так счастлив, лол!!!!!!!!!
проблема в глюперспективе (45.0f,(GLfloat) ширина /(GLfloat) высота,0.001f,100.0f);
как сказал GuentherKrass в http://www.opengl.org/discussion_boards/showthread.php/146157-Z-Fail-Stencil-Shadow-Volumes
Если вы сделаете это таким образом, обязательно используйте матрицу перспективной проекции с бесконечной дальней плоскостью или используйте GL_DEPTH_CLAMP, чтобы избежать отбрасывания задней крышки с помощью дальней плоскости отсечения.
поэтому я просто изменил код выше
gluPerspective(45.0f,(GLfloat) ширина /(GLfloat) высота,0.001f,1000000.0f);
хорошо, это выглядит идеально!!!!!!!!!!!!!!!!! 111 хахахахааа
два дня , не ложиться спать , лапша быстрого приготовления.. черт возьми, так стоит!!
хорошо, я выложу последнюю эффективную картинку. Если кому-то нужен мой код, просто напишите мне (nomorefancy@gmail.com)
внимание: тень кирпича не зависит от тени стены.