伪体积光照
1、 引言(Introduction)
在今天的许多演示及游戏中,你可能会看到各种类型的体积效果,就如上图所示。一个可行的想法是使用数学技巧也能做出如此令人惊奇的效果。该文的目的就是给你这个演示的实现方法 ― 它真的很容易。
In many demos and games these days you see various volumetric effects like on the picture above. One might think that it takes amazing skills in both programming as well as mathematics to do such effects. The aim of this tutorial is to show one way of doing it - and it's even an easy way!
2、理论(Theory)
首先,请你看一眼这两张纹理:
First, take a look at these two textures:
左图
右图
这是我们要做的事情:
This is what we do:
1)使用彩色纹理绘制对象“射出”的光(左图)。
Draw the object 'emitting' the light using the colour-texture (the one on the left).
2)稍微变化对象的比例。
Slightly scale the object.
3)在混合方式下使用掩码纹理绘制对象(右图)。
Draw the object using the mask-texture (the one on the right) in a blend mode.
4)重复2与3点 ― 你重复次数愈多,它看起来会更加漂亮(上面的效果我循环了50次)。
Repeat points 2 and 3 - the more times you repeat them, the better it looks (the image above loops 50 times).
3、实现(Implementation)
这方法不受任何特定应用程序接口(它将会与硬件及软件一起工作)的限制,但是我想声明的是,它受到象素填充率的限制(在GF3的640x480下运行为50帧,而Quadro2上仅仅为25帧)。
This method is not limited to any specific API's (it'll work with both hardware and software), apart from the fact, that it is *very* fillrate limited (i get 50fps on gf3 in 640x480, but only 25fps on quadro2).
这里是核心的OpenGL代码(你只需将其放进你自己的程序框架或演示系统中):
Here is the needed code for OpenGL (you need to put this into your own framework or demosystem):
GLuint texture[2];
// Load the textures into this array.
GLUquadricObj *sphere;
// This is our object - you can use your own geometric data if you like.
// Call this function from your initroutine.
void InitQuadric()
// Initialise the quadric object.
{
sphere = gluNewQuadric();
// Create new quadric.
gluQuadricNormals(sphere, GLU_SMOOTH);
// Set normals to be smooth.
gluQuadricTexture(sphere, GL_TRUE);
// Enable texturemapping.
}
// This is the main routine - call this every frame.
void DrawVolLight()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Clear screen and depthbuffer
static float angle = 0.0f;
// Variable to control rotation of the object.
glLoadIdentity();
// Reset the modelview matrix.
glTranslatef(0.0f, 0.0f, -15.0f);
// Move the object in place.
glRotatef(angle, 1.2f, 0.4f, 0.7f);
// Rotate the object.
glEnable(GL_TEXTURE_2D);
// Enable texturemapping.
glBindTexture(GL_TEXTURE_2D, texture[0]);
// Use the colour-texture.
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
// Full colour and no blending.
gluSphere(sphere, 2, 32, 16);
// Draw the object.
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
// Setup blending.
glBindTexture(GL_TEXTURE_2D, texture[1]);
// Use the mask-texture.
glEnable(GL_BLEND);
// Turn on blending.
glDepthMask(GL_NEVER);
// Don't write to depth buffer
static float numlayers = 50;
// This it the amount of loops - higher values equals better looking (and slower) light.
for (float n = 0; n
// The loop!
{
float scale = 1+n/numlayers;
// calculate the scale-factor.
float color = 1.0f/n+sin(angle/10)/16; // Calculate the percentage of colour used in the blending.
glLoadIdentity();
// Reset the modelview matrix.
glTranslatef(0.0f, 0.0f, -15.0f);
// Position the object.
glRotatef(angle, 1.2f, 0.4f, 0.7f);
// Rotate the object.
glScalef(scale, scale, scale);
// Scale the object (the coordinatesystem actually)
glColor4f(color, color, color, 1.0f);
// Set the amount of colour (used for blending).
gluSphere(sphere, 2, 32, 16);
// Draw the object using the mask-texture
}
glDepthMask(GL_TRUE);
// Make it possilbe to write to the depth-buffer again.
glDisable(GL_BLEND);
// Disable blending.
angle = 0.02*(GetTickCount()-startticks);
// Set up rotation angle for the next fram
glFlush();
// Flush the GL-pipeline (force GL to finish everything it's doing).
}
在实践中,我可能需要调整光照体积。其由调节glColor4f的值完成。
In my implementation, i have made it possible to adjust the volume of the light. It is done by modulating the glColor4f values.
这也就是光源看起来为什么好象在搏动的原因。
This is what makes the light look like it's pulsing.
4、后记(Afterword)
我知道的这个方法是使用了巨量的填充率,它可能会不适合以后的游戏。但是请你注意,它看起来非常好,而且实现过程简单。假如你有任何改进或建议欢迎给我来信。对了,文末的附带的资源是一个执行版(包括纹理)。
I know that this method is a fillrate-eater and that it is probably not suitable for games for the next year or two. Nevertheless! It looks good and it is not too hard to do. Improvements / suggestions are welcome! Let me know if you make you own implementation.Download.Binary version (including textures) of my implementation can be downloaded here