是否可以一次通过多个视图渲染对象

Posted

技术标签:

【中文标题】是否可以一次通过多个视图渲染对象【英文标题】:Is it possible to render an object from multiple views in a single pass 【发布时间】:2014-07-31 12:15:08 【问题描述】:

我需要从多个视图渲染场景的深度,是否可以在单个渲染通道中完成?

深度值可以保存为任何形式的二维纹理。

【问题讨论】:

如果我理解正确,请告诉我。您想在一次渲染过程中为同一场景使用不同的相机位置渲染深度缓冲区? @MichaelCMS 是的,你是对的。我想在着色器程序中进行。 @shapeare:您是否可以发布所需/实现的渲染的屏幕截图?这将有助于理解问题和相同的用例 @ppu.spu 抱歉,我还没有完成实现。我刚刚在聚光灯案例中实现了常规阴影贴图算法。我想通过用几个聚光灯来近似它来扩展我的程序以使用区域光,这就是我尝试从几个聚光灯位置渲染几何图形的原因。出于效率的考虑,我更喜欢一次性渲染所有这些聚光灯的阴影贴图。 【参考方案1】:

这应该可以通过一点创造力来实现。随着 ARB_viewport_array (glViewportIndexed, glDepthRangeIndexed) 在 opengl 3.2 上的引入,可以指定多个视口(准确地说最多六个,因此可以在一次通过中渲染立方体贴图)。在geometry shader 的帮助下,可以复制几何图形以定位特定的视口/图层,另请参阅gl_ViewportIndex、gl_Layer。根据您确切想要渲染的方式,即分屏或分离的缓冲区,您可能需要有条件地将片段输出到不同的缓冲区或相应的屏幕的不同部分。另见Rendering to cube map

【讨论】:

【参考方案2】:

您也许可以使用几何着色器,并将视口分解为单独的子窗口来实现。例如,您可以从 4 个不同的视图渲染场景,并将每个视图渲染为当前视口的 1/4(例如,视口的左上角四分之一将是场景的一个“视图”)。

您将拥有一个直通顶点着色器,然后将 4 个模型-视图-投影矩阵作为统一分配给几何着色器。几何着色器会将三角形作为输入,对于每个输入三角形,输出 4 个三角形(12 个顶点):

#version 150 core
uniform mat4 MVP_TopLeft;
uniform mat4 MVP_TopRight;
uniform mat4 MVP_BottomLeft;
uniform mat4 MVP_BottomRight;
layout(triangles) in;
layout(GL_TRIANGLES​, max_vertices = 12​) out;
out int SubWindowID;
void main(void) 
    SubWindowID = 0;
    for (int i = 0; i < 3; i++) 
        gl_Position = MVP_TopLeft*gl_in[i].gl_Position;
        //Shift the coordinates into the mini-window
        gl_Position.x = (0.5*gl_Position.x - 0.5);
        gl_Position.y = (0.5*gl_Position.y + 0.5);
        EmitVertex();
    
    EmitPrimitive();

    SubWindowID = 1;
    for (int i = 0; i < 3; i++) 
        gl_Position = MVP_TopRight*gl_in[i].gl_Position;
        //Shift the coordinates into the mini-window
        gl_Position.x = (0.5*gl_Position.x + 0.5);
        gl_Position.y = (0.5*gl_Position.y + 0.5);
        EmitVertex();
    
    EmitPrimitive();

    SubWindowID = 2;
    for (int i = 0; i < 3; i++) 
        gl_Position = MVP_BottomLeft*gl_in[i].gl_Position;
        //Shift the coordinates into the mini-window
        gl_Position.x = (0.5*gl_Position.x - 0.5);
        gl_Position.y = (0.5*gl_Position.y - 0.5);
        EmitVertex();
    
    EmitPrimitive();

    SubWindowID = 3;
    for (int i = 0; i < 3; i++) 
        gl_Position = MVP_BottomRight*gl_in[i].gl_Position;
        //Shift the coordinates into the mini-window
        gl_Position.x = (0.5*gl_Position.x + 0.5);
        gl_Position.y = (0.5*gl_Position.y - 0.5);
        EmitVertex();
    
    EmitPrimitive();

然后,在片段着色器中,您将在 main 函数上方声明 in int SubWindowID;,并且该 int 将对应于该片段被绘制在哪个子窗口中。

【讨论】:

在片段着色器中,如何知道片段应该渲染到哪个视口? 通过阅读,我发现GS实际上可以输出一个名为gl_ViewportIndex的变量,我可能会依赖这个。因此,在片段着色器中,我可能会写: if(gl_ViewportIndex == 0) texture0 = vec4(depth);否则 if(gl_ViewportIndex == 1) texture1 = vec4(depth)..... 类似这样的东西。我在正确的轨道上吗? 所有片段都会渲染到实际视口(由 glViewport 设置的那个),但是如果您需要确定片段渲染到哪个子窗口,您可以从几何着色器传递一个属性.上面的答案将很快被编辑以帮助解决这个问题。 抱歉,我应该澄清一下:在上面的示例中,只使用了一个视口,尽管可以使用多个视口作为替代方法 废话,我刚刚意识到为什么这可能不起作用:三角形的剪裁将针对整个视口而不是单个子窗口进行。因此,如果您在左上角的子窗口中有一个三角形延伸到右上角的子窗口,则整个三角形都会被渲染,即使是应该被切断的部分。很抱歉,这可能是朝错误方向迈出的一步。【参考方案3】:

由于渲染管道的工作方式,您的建议是不可能的:

您将一堆顶点发送到管道 在顶点着色阶段,显卡取入投影矩阵,将每个顶点按照投影矩阵统一放置在投影空间中的位置 现在在投影空间中正确设置了顶点,光栅化发生并出现片段,因此片段着色阶段开始 片段写入深度缓冲区

您的建议意味着第 2 阶段有两个输出而不是一个,这是不可能的。您可以将场景中每个摄像机的投影矩阵发送到着色器,但投影空间中只有一个出口顶点(顶点着色器的最终结果是空间中的一个位置,而不是一组位置)。

【讨论】:

以上是关于是否可以一次通过多个视图渲染对象的主要内容,如果未能解决你的问题,请参考以下文章

我可以通过 webService 调用获取网格视图的渲染 html 吗?

threejs webgl性能优化

Partials vs for 循环——最佳实践

如何返回通过django中的视图的静态文件?

如何返回通过django中的视图的静态文件?

使用 jquery 重新渲染页面