Oculus 裂谷 + 点精灵 + 点大小衰减
Oculus Rift + Point Sprites + Point size attenuation
我正在编写一个支持 Oculus Rfit 的小项目,我使用点精灵来渲染我的粒子。我根据点精灵与顶点着色器中"相机"的距离来计算点精灵的大小(以像素为单位)。在默认屏幕(而不是在 Rift 上)绘图时,大小可以完美运行,但是当我切换到 Rift 时,我注意到以下现象:
左眼上的颗粒很小,并且尺寸会很快减小。 右眼上的颗粒很大,大小不会改变。
截图:裂痕禁用:https://i.stack.imgur.com/03l3o.jpg启用裂缝:https://i.stack.imgur.com/4tswC.jpg
下面是顶点着色器:
#version 120
attribute vec3 attr_pos;
attribute vec4 attr_col;
attribute float attr_size;
uniform mat4 st_view_matrix;
uniform mat4 st_proj_matrix;
uniform vec2 st_screen_size;
varying vec4 color;
void main()
{
vec4 local_pos = vec4(attr_pos, 1.0);
vec4 eye_pos = st_view_matrix * local_pos;
vec4 proj_vector = st_proj_matrix * vec4(attr_size, 0.0, eye_pos.z, eye_pos.w);
float proj_size = st_screen_size.x * proj_vector.x / proj_vector.w;
gl_PointSize = proj_size;
gl_Position = st_proj_matrix * eye_pos;
color = attr_col;
}
st_screen_size制服是视口的大小。由于我在 Rift 上渲染时使用单个 frambuffer(每只眼睛 1 个一半),因此st_screen_size的值应该是 (frabuffer_width/2.0, frambuffer_height)。
这是我的抽奖电话:
/*Drawing starts with a call to ovrHmd_BeginFrame.*/
ovrHmd_BeginFrame(game::engine::ovr_data.hmd, 0);
/*Start drawing onto our texture render target.*/
game::engine::ovr_rtarg.bind();
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Update the particles.
game::engine::nuc_manager->update(dt, get_msec());
/*for each eye... */
for(unsigned int i = 0 ; i < 2 ; i++){
ovrEyeType eye = game::engine::ovr_data.hmd->EyeRenderOrder[i];
/* -- Viewport Transformation --
* Setup the viewport to draw in the left half of the framebuffer when we're
* rendering the left eye's view (0, 0, width / 2.0, height), and in the right half
* of the frambuffer for the right eye's view (width / 2.0, 0, width / 2.0, height)
*/
int fb_width = game::engine::ovr_rtarg.get_fb_width();
int fb_height = game::engine::ovr_rtarg.get_fb_height();
glViewport(eye == ovrEye_Left ? 0 : fb_width / 2, 0, fb_width / 2, fb_height);
//Send the Viewport size to the shader.
set_unistate("st_screen_size", Vector2(fb_width /2.0 , fb_height));
/* -- Projection Transformation --
* We'll just have to use the projection matrix supplied but he oculus SDK for this eye.
* Note that libovr matrices are the transpose of what OpenGL expects, so we have to
* send the transposed ovr projection matrix to the shader.*/
proj = ovrMatrix4f_Projection(game::engine::ovr_data.hmd->DefaultEyeFov[eye], 0.01, 40000.0, true);
Matrix4x4 proj_mat;
memcpy(proj_mat[0], proj.M, 16 * sizeof(float));
//Send the Projection matrix to the shader.
set_projection_matrix(proj_mat);
/* --view/camera tranformation --
* We need to construct a view matrix by combining all the information provided by
* the oculus SDK, about the position and orientation of the user's head in the world.
*/
pose[eye] = ovrHmd_GetHmdPosePerEye(game::engine::ovr_data.hmd, eye);
camera->reset_identity();
camera->translate(Vector3(game::engine::ovr_data.eye_rdesc[eye].HmdToEyeViewOffset.x,
game::engine::ovr_data.eye_rdesc[eye].HmdToEyeViewOffset.y,
game::engine::ovr_data.eye_rdesc[eye].HmdToEyeViewOffset.z));
/*Construct a quaternion from the data of the oculus SDK and rotate the view matrix*/
Quaternion q = Quaternion(pose[eye].Orientation.w, pose[eye].Orientation.x,
pose[eye].Orientation.y, pose[eye].Orientation.z);
camera->rotate(q.inverse().normalized());
/*Translate the view matrix with the positional tracking*/
camera->translate(Vector3(-pose[eye].Position.x, -pose[eye].Position.y, -pose[eye].Position.z));
camera->rotate(Vector3(0, 1, 0), DEG_TO_RAD(theta));
//Send the View matrix to the shader.
set_view_matrix(*camera);
game::engine::active_stage->render(STAGE_RENDER_SKY | STAGE_RENDER_SCENES | STAGE_RENDER_GUNS |
STAGE_RENDER_ENEMIES | STAGE_RENDER_PROJECTILES, get_msec());
game::engine::nuc_manager->render(RENDER_PSYS, get_msec());
game::engine::active_stage->render(STAGE_RENDER_COCKPIT, get_msec());
}
/* After drawing both eyes into the texture render target, revert to drawing directly to the display,
* and we call ovrHmd_EndFrame, to let the Oculus SDK draw both images properly, compensated for lens
* distortion and chromatic abberation onto the HMD screen.
*/
game::engine::ovr_rtarg.unbind();
ovrHmd_EndFrame(game::engine::ovr_data.hmd, pose, &game::engine::ovr_data.fb_ovr_tex[0].Texture);
这个问题已经困扰了我很多天了......我觉得我已经走到了死胡同。我可以用广告牌四边形.....但我不想轻易放弃:)加点精灵更快。在 Rift 上渲染时,基于距离的点大小衰减背后的数学会发生变化吗?我不是考虑到什么吗?数学不是(至少)我的强项。:)任何见解将不胜感激!
PS:如果需要有关我发布的代码的任何其他信息,我很乐意提供。
我可以推荐几种故障排除技术。
首先,修改代码以自动编写渲染的第一帧的屏幕截图(或者如果不方便,只需使用一个静态布尔值,该值会导致主绘制跳过除第一次运行后开始/结束帧调用之外的所有内容。 SDK 有时会弄乱 OpenGL 状态机,如果发生这种情况,那么您所看到的可能是 ovrHmd_EndFrame() 中所做的工作在随后通过渲染循环时搞砸渲染的结果。 渲染代码中的其他内容(在粒子渲染之后)可能会无意中恢复所需的状态,这就是渲染的第二只眼睛看起来正常的原因。
其次,我会尝试将渲染的眼睛分成两个帧缓冲区。 也许您的代码中某些东西意外地对整个帧缓冲器执行了某些操作(例如清除深度缓冲区),从而导致了差异。 您可能正在通过第二只眼运行,帧缓冲的状态与您基于顶级代码的预期不同。 分成两个帧缓冲会告诉你是否是这种情况。
与第二个测试类似,您可以运行的另一个测试是重构渲染代码,以允许您使用默认帧缓冲区通过此循环,而无需 Oculus SDK 调用。 这是另一种技术,可帮助您确定问题出在 SDK 还是您自己的渲染代码中。 只需将两只眼睛视图渲染到屏幕的两半,而不是屏幕外帧缓冲的两半。
vec4 local_pos = vec4(attr_pos, 1.0); vec4 eye_pos = st_view_matrix * local_pos; vec4 proj_voxel = st_proj_matrix * vec4(attr_size, 0.0, eye_pos.z, eye_pos.w); float proj_size = st_screen_size.x * proj_voxel.x / proj_voxel.w;
gl_PointSize = proj_size;
基本上,您首先将点转换为视图空间,以找出它在视图空间中的Z坐标(与查看器的距离),然后构建一个与X轴对齐的具有所需粒度的矢量,并投影以查看它在投影和视口转换时覆盖了多少像素(排序)。
这是完全合理的,假设你的投影矩阵是对称的。在处理裂痕时,这种假设是错误的。我画了一个图表来更好地说明这个问题:
https://i.stack.imgur.com/aLKkx.jpg
如您所见,当视锥体不对称时(裂缝肯定就是这种情况),使用投影点与屏幕中心的距离会为每只眼睛提供截然不同的值,并且肯定与您正在寻找的"正确"投影大小不同。
相反,您必须做的是使用相同的方法投影两个点,例如 (0, 0, z, 1) 和 (attr_size, 0, z, 1),并计算它们在屏幕空间中的差值(投影、透视分割和视口之后)。
- OpenGL 着色器不绘制任何东西 - 糟糕的精灵类
- 为什么这些函数不衰减到函数指针?
- 从精灵表在 sfml 中为精灵制作动画
- 使用衰减与完美转发
- AABB 碰撞使实际精灵的大小翻倍
- 如何在精灵表上的两个不同部分之间来回切换,同时用户仍使用 SFML 持有密钥
- 在完美转发中需要衰减
- 我的精灵使用名为 ASGE 的C++框架卡在屏幕边缘
- 为什么多维数组中的空字符串文本衰减为空指针?
- 如何为精灵编写复制构造函数
- 绘制精灵会导致分割错误
- 衰减到基类
- 精灵动画查看器应该使用哪些Qt小部件
- 从原始指针(衰减的 C 样式数组)和大小生成范围::视图
- 数组变量衰减为指针
- 从语言设计层面来看,当编译时无法推断条件时,为什么"if constexpr"不衰减到"trival if"
- Eclipse构建但不会启动精灵
- OpenGL 2D游戏只绘制第二个精灵纹理而不是第一个
- 打开 gl 精灵类不起作用
- Oculus 裂谷 + 点精灵 + 点大小衰减