半透明模型中的OpenGL(ES)模型

时间:2012-03-14 23:01:40

标签: opengl-es translucency

我想要“面对水晶球”效果,我有一个模型(面部)在半透明模型(水晶球)内做事。我觉得我正在服用疯狂的药片,因为我只是不能让这个内脸被球部分遮挡。我的目标是改变球(和/或脸)的alpha值,使脸部出现并消失。

以下是相关位代码。正如你所看到的,我不是使用着色器,而是使用旧的GL / GLES1。如果有人能告诉我我做错了什么,我将非常感激。

设置代码......

//-- CONFIGURATION ---------------
// Create The Depth Buffer Object
glGenRenderbuffersOES(1, &depth_renderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, depth_renderbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, 
                         GL_DEPTH_COMPONENT16_OES, 
                         width, 
                         height);

// Create The FrameBuffer Object
glGenFramebuffersOES(1, &framebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, framebuffer);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
                             GL_COLOR_ATTACHMENT0_OES,
                             GL_RENDERBUFFER_OES, 
                             color_renderbuffer);

glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, 
                             GL_DEPTH_ATTACHMENT_OES, 
                             GL_RENDERBUFFER_OES, 
                             depth_renderbuffer);

// Bind Color Buffer
glBindRenderbufferOES(GL_RENDERBUFFER_OES, color_renderbuffer);

glViewport(0, 0, width, height);

//-- LIGHTING ----------------------
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0); 

//-- PROJECTION ---------------------
glMatrixMode(GL_PROJECTION);
viewport_size = vec2((float) width,(float) height);

//Orthographic Projection
float max_x,max_y;
if(width>height){
    max_y = 1;
    max_x = (float)width/(float)height;
}
else{
    max_x = 1;
    max_y = (float)height/(float) width;
}
const float MAX_X = max_x;
const float MAX_Y = max_y;
const float Z_0 = 0;
const float MAX_Z = 1; 
glOrthof(-MAX_X, MAX_X, -MAX_Y, MAX_Y, Z_0-MAX_Z, Z_0+MAX_Z);

world_size = vec3(2*MAX_X,2*MAX_Y,2*MAX_Z);

//Color Depth
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE); //Dissapears if False
glDepthFunc(GL_LEQUAL);
glEnable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //doesn't do it
glBlendFunc(GL_ONE, GL_ONE); //better

这是渲染调用

glClearColor(world->background_color.x,
             world->background_color.y,
             world->background_color.z,
             world->background_color.w);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

for(int s=0;s<surfaces.size();s++){
    Surface* surface = surface[s];

    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, surface->getMatAmbient().Pointer());
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, surface->getMatDiffuse().Pointer());

    glMatrixMode(GL_MODELVIEW);

    //If I don't put this code in here (as opposed to above), the light gets all crazy! WHY!?
    glPushMatrix();
    glLoadIdentity();
    vec4 light_position = vec4(world->light->position,1);
    glLightfv(GL_LIGHT0,GL_POSITION,light_position.Pointer());

    glPopMatrix();


    glPushMatrix();
    glMultMatrixf(surface->transform.Pointer());

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surface->index_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, surface->vertex_buffer);

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);

    glVertexPointer(3, GL_FLOAT, VERTEX_STRIDE, 0);
    glNormalPointer(GL_FLOAT, VERTEX_STRIDE, (GLvoid*) VERTEX_NORMAL_OFFSET);

    glDrawElements(GL_TRIANGLES, surface->indices.size(), GL_UNSIGNED_SHORT, 0);


    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glPopMatrix();

}

2 个答案:

答案 0 :(得分:3)

听起来你可能会遇到一个深度缓冲概念的简单案例,并不适用于你的场景。深度缓冲区为屏幕上的每个像素存储一个深度,在具有完全不透明对象的场景中,该深度将是该像素处最近对象的深度。

问题在于,当您想要将部分透明的对象添加到场景中时,您最终会处于多个对象对单个像素的颜色有贡献的位置。但是你仍然只能存储其中一个的深度。

所以在你的情况下可能发生的事情是你首先绘制水晶球,然后将各种水晶球像素的深度放入深度缓冲区。然后你试图绘制面部,OpenGL看到它比缓冲区中的值更远,所以跳过那些像素。

因此,快速修复解决方案只是手动重新排序场景几何体,使得面部始终在水晶球之前绘制,始终在内部。

在一个理想的解决方案中,您可以在一个步骤中绘制所有不透明几何体(传统上以接近前后顺序的东西,虽然在PowerVR上并不重要)来建立不透明的深度值,然后是所有透明几何体回到前面,以便按正确的顺序合成。

在OpenGL中,您确实希望某些事物的顺序相对固定,以便您可以将相关值推送到GPU而不会产生通信成本。人们仍倾向于划分为不透明和透明的几何体,并首先绘制不透明的但通常它们只会在绘制透明几何体时禁用z缓冲区写入,努力做到有点像后向前的顺序但是不要在问题上投入太多时间。

如果您乐意使用纯添加剂混合,那么一旦深度缓冲区设置了不透明的东西,透明胶片的任何订单图纸都是正确的。

答案 1 :(得分:2)

您渲染对象的顺序是什么?如果你在脸前画球,那么整个脸都会被拒绝,因为它在z缓冲区的球后面。如果要进行正确的透明度,则必须从后向前渲染对象。

关于你的内联问题: //If I don't put this code in here (as opposed to above), the light gets all crazy! WHY!?

当您使用位置调用glLightfv时,位置将根据当前在模型视图矩阵堆栈中的内容进行转换。你必须把它放在相对于你定义坐标的参考框架的正确位置(它是相对于视图坐标,还是世界坐标,还是对象坐标?)。