2015-11-20 173 views
4

我想了解使用矩阵的opengl中的摄像头。OpenGL模型,视图,投影矩阵

我写了一个简单的着色器,看起来像这样:

#version 330 core 

layout (location = 0) in vec3 a_pos; 
layout (location = 1) in vec4 a_col; 

uniform mat4 u_mvp_mat; 
uniform mat4 u_mod_mat; 
uniform mat4 u_view_mat; 
uniform mat4 u_proj_mat; 

out vec4 f_color; 

void main() 
{ 
    vec4 v = u_mvp_mat * vec4(0.0, 0.0, 1.0, 1.0); 
    gl_Position = u_mvp_mat * vec4(a_pos, 1.0); 
    //gl_Position = u_proj_mat * u_view_mat * u_mod_mat * vec4(a_pos, 1.0); 
    f_color = a_col; 
} 

这是一个有点冗长,但那是因为我测试通过无论是在模型,视图或投影矩阵,做乘法的GPU或者在cpu上进行乘法运算并传入mvp矩阵,然后执行mvp *位置矩阵乘法。

据我所知,后者可以提供性能提升,但绘制1个quad我并没有在这一点上看到任何性能问题。

现在我使用此代码从我的着色器中获取位置并创建模型视图和投影矩阵。

pos_loc = get_attrib_location(ce_get_default_shader(), "a_pos"); 
col_loc = get_attrib_location(ce_get_default_shader(), "a_col"); 
mvp_matrix_loc = get_uniform_location(ce_get_default_shader(), "u_mvp_mat"); 
model_mat_loc = get_uniform_location(ce_get_default_shader(), "u_mod_mat"); 
view_mat_loc = get_uniform_location(ce_get_default_shader(), "u_view_mat"); 
proj_matrix_loc = 
    get_uniform_location(ce_get_default_shader(), "u_proj_mat"); 

float h_w = (float)ce_get_width() * 0.5f; //width = 320 
float h_h = (float)ce_get_height() * 0.5f; //height = 480 

model_mat = mat4_identity(); 
view_mat = mat4_identity(); 
proj_mat = mat4_identity(); 

point3* eye = point3_new(0, 0, 0); 
point3* center = point3_new(0, 0, -1); 
vec3* up = vec3_new(0, 1, 0); 

mat4_look_at(view_mat, eye, center, up); 
mat4_translate(view_mat, h_w, h_h, -20); 

mat4_ortho(proj_mat, 0, ce_get_width(), 0, ce_get_height(), 1, 100); 

mat4_scale(model_mat, 30, 30, 1); 

mvp_mat = mat4_identity(); 

之后我设置我的vao和vbo的然后准备做渲染。

glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 
glClear(GL_COLOR_BUFFER_BIT); 
glUseProgram(ce_get_default_shader()->shader_program); 
glBindVertexArray(vao); 

mvp_mat = mat4_multi(mvp_mat, view_mat, model_mat); 
mvp_mat = mat4_multi(mvp_mat, proj_mat, mvp_mat); 

glUniformMatrix4fv(mvp_matrix_loc, 1, GL_FALSE, mat4_get_data(mvp_mat)); 

glUniformMatrix4fv(model_mat_loc, 1, GL_FALSE, mat4_get_data(model_mat)); 
glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, mat4_get_data(view_mat)); 
glUniformMatrix4fv(proj_matrix_loc, 1, GL_FALSE, mat4_get_data(proj_mat)); 

glDrawElements(GL_TRIANGLES, quad->vertex_count, GL_UNSIGNED_SHORT, 0); 
glBindVertexArray(0); 

假设所有矩阵数学是正确的,我想抽象视图和投影矩阵出到照相机结构以及模型矩阵为精灵结构体,这样我可以避免所有这些矩阵数学和使事情更容易使用。

该矩阵乘法的顺序是:

Projection * View * Model * Vector 

所以当精灵保持模型矩阵相机将持有投影和视图矩阵。

然后在将数据发送到GPU进行矩阵乘法之前,先进行所有相机转换和精灵转换。

如果我记得矩阵乘法不可交换所以 view * projection * model会导致错误的结果矩阵。

伪代码

glClearxxx(....); 
glUseProgram(..); 
glBindVertexArray(..); 

mvp_mat = mat4_identity(); 
proj_mat = camera_get_proj_mat(); 
view_mat = camera_get_view_mat(); 
mod_mat = sprite_get_transform_mat(); 

mat4_multi(mvp_mat, view_mat, mod_mat); //mvp holds model * view 
mat4_multi(mvp_mat, proj_mat, mvp_mat); //mvp holds proj * model * view 

glUniformMatrix4fv(mvp_mat, 1, GL_FALSE, mat4_get_data(mvp_mat)); 

glDrawElements(...); 
glBindVertexArray(0); 

那是一个高性能的方式去这样做是可扩展的?

回答

2

这是一个高性能的方式去做这个可扩展的吗?

是的,除非你有一个非常特殊的用例,这与标准非常不一样。

您应该经常担心的最后一件事是关于从相机中检索模型视图和投影矩阵的性能。

这是因为这些矩阵通常只需要每个视口每帧获取一次。在scanline-rasterizing基元的时候,在一帧中可能会出现数百万次的其他工作,并且将矩阵拉出相机只是一个简单的恒定时间操作。

所以通常情况下,您只是想尽可能方便。在我的情况下,我通过一个中央SDK中的函数指针的抽象接口,然后函数即时计算 proj/mv/ti_mv矩阵,从用户定义的与摄像头相关的属性中。尽管如此,它从来没有作为一个热点出现 - 它甚至都没有出现在剖析器中。

有更昂贵的东西需要担心。可伸缩性意味着规模 - 从相机中检索矩阵的复杂性不会缩放。要渲染的三角形或四边形或线条或其他基元的数量可以缩放,碎片着色器中处理的碎片数量可以缩放。相机通常不会缩放,除非相对于视口的数量而言,并且任何人都无法使用一百万个视口。

+0

我明白了你的可扩展性,它确实有道理。还有一个问题。假设我喜欢你的建议,并让相机返回一个视图*投影矩阵,然后不会乘以模型矩阵产生错误的结果,因为它会说'model * proj * view',这不是正确的方法做矩阵乘法。我想有可能将模型矩阵传递给相机,并让它做正确的事情,但这样会使精灵系列非常靠近相机,不是吗? – user1610950

+0

或者我会建议,从相机界面的水平,返回投影和模型视图矩阵。当对照相机进行渲染时,您可以在现场计算其余部分(例如:用于正常计算的转置逆模型视图)。 –

+0

对于以正确的顺序相乘矩阵,是的 - 你必须这样做。但我不太清楚为什么这里有一个问题 - 只需要按照获得正确结果所需的顺序将它们相乘即可。或者我错过了一些难以做到的事情? –

2

我还没有检查位按位,但它通常看起来确定你在做什么。

我想抽象视图和投影矩阵伸到相机结构

这是一个最合适的想法;我几乎无法想象一个没有这种抽象的严肃的GL应用程序

这是一个高性能的方式去做这个可扩展的吗?

伸缩性的一般约束是

  • 漫射和镜面BRDFs(其也需要,顺便说一句,光均匀,一个正常的属性和计算正常矩阵if the scaling of the model is non-uniform的),并需要per-pixel illumination质量渲染。

  • 相同多重照明(例如太阳和密切的聚光灯)

  • 阴影贴图!阴影贴图? (每个光源?)

  • 透明度

  • 反射(镜子,玻璃,水)

  • 纹理

正如你是否可以从列表中只有一个MVP统一体和一个顶点坐标属性,你不会走得太远。

但制服的单纯数量是迄今为止不是性能的最关键点 - 看到你的代码,我敢肯定,只有在需要时您将无法重新编译着色器不必要的,更新的制服,使用Uniform Buffer Objects等。

问题是插入这些制服和维也纳组织的数据。或不。


考虑人形网“爱丽丝”运行(这是一个网状变形+翻译)跨城市广场上的大风(水会有涟漪)晚上(不止一个相关的光源),通过一个喷泉。

让考虑到我们收集这一切由CPU和老派的一切手段只插即用数据呈现到着色器:

  • Alice的网格演变,因此她的维也纳组织需要更新
  • 爱丽丝的网格会移动;因此所有受影响的阴影贴图都需要更新(确定,假设它们是由GPU上的阴影照明循环生成的,但是如果你做错了方式,你会在左右推送大量数据)
  • Alice在喷泉会来来去去
  • 翘的头发会被漩涡 - CPU可能有安静的一个繁忙的时间,至少可以说是

(实际上后者是如此的困难,你将几乎看不到任何中途逼真实时长时间开放的头发动画,但令人惊讶(不,不是真的)许多小马尾巴和短发)

我们还没有谈到爱丽丝的装束;让我们只希望她穿着T恤和牛仔裤(不是宽衬衫和裙子,这需要折叠和碰撞计算)。

正如你可能已经猜到,老派的做法并没有把我们带到很远的地步,因此,在CPU和GPU之间找到适合的操作。

此外,应该考虑在早期阶段对计算进行并行化。将数据尽可能地平整为合理的大小是有利的,因此只需将指针和大小放入gl-call并且投标数据就可以在没有任何复制,重新排列,循环或更进一步的情况下告别。 。

这是我今天对GL性能和可扩展性的2分钱智慧。

+0

你在这个答案中提出的要点实际上是最初让我写这个问题的。虽然看起来离我目前的实施还有很远的距离,但我宁愿避免把自己挖到沟里去。有很多事情需要考虑,谢谢,我赞成这个答案。 – user1610950