Java 延迟着色在LWJGL中究竟是如何工作的?

Java 延迟着色在LWJGL中究竟是如何工作的?,java,opengl,glsl,deferred-rendering,Java,Opengl,Glsl,Deferred Rendering,我想用GLSL、Java和openGl启动一个延迟着色项目 一,。延迟渲染管道是如何工作的,它是否为每个图像渲染场景? 例如,当我想要创建镜面反射、模糊和阴影纹理时,我是否需要为每个纹理渲染场景 我看到了一些代码片段,其中没有多个渲染循环 二,。什么是几何缓冲区?它是做什么的?它是否类似于场景数据的存储,我可以在不再次渲染的情况下将其绘制到纹理?延迟渲染的基本思想是将网格几何体转换为目标帧缓冲区上的位置的过程分开,并为目标帧缓冲区的像素提供最终颜色 第一步是以某种方式渲染几何体,帧缓冲区的每个像

我想用GLSL、Java和openGl启动一个延迟着色项目

一,。延迟渲染管道是如何工作的,它是否为每个图像渲染场景? 例如,当我想要创建镜面反射、模糊和阴影纹理时,我是否需要为每个纹理渲染场景

我看到了一些代码片段,其中没有多个渲染循环


二,。什么是几何缓冲区?它是做什么的?它是否类似于场景数据的存储,我可以在不再次渲染的情况下将其绘制到纹理?

延迟渲染的基本思想是将网格几何体转换为目标帧缓冲区上的位置的过程分开,并为目标帧缓冲区的像素提供最终颜色

第一步是以某种方式渲染几何体,帧缓冲区的每个像素接收有关原始几何体的信息,即世界或眼睛空间中的位置(首选眼睛空间)、变换的切线空间(法线、切线、副法线)和其他属性,具体取决于以后需要的属性。这是“几何缓冲区”(也回答了你的2.问题)

有了几何缓冲区,预计算的几何体→像素映射可以重复用于几个类似的处理步骤。例如,如果要渲染50个光源,则只需处理几何体50次(相当于渲染100个三角形,这是现代GPU的儿童游戏),每次迭代都使用其他参数(灯光位置、方向、阴影缓冲区等)。这与常规多通道渲染不同,在常规多通道渲染中,每次迭代都需要重新处理整个几何体

当然,每个过程都可以用于渲染不同类型的着色过程(辉光、模糊、波基、光晕等)

然后,对于每个迭代过程,结果都合并到一个合成图像中。

1)延迟着色涉及将场景的几何体渲染和基本上所有其他内容分离到单独的过程中

例如,当我想要创建镜面反射、模糊和阴影纹理时,我是否需要为每个纹理渲染场景

对于阴影纹理,可能(如果使用阴影贴图,这是不可避免的)。但对于其他一切:

不,这就是延迟着色如此有用的原因。在延迟管道中,渲染几何体一次,并保存每个像素的颜色、法线和三维位置(几何体缓冲区)。这可以通过两种不同的方式实现,但最常见的是将帧缓冲区对象()与多个渲染目标(MRT)一起使用。使用FBO进行延迟着色时,渲染地貌的方式与正常渲染完全相同,只是绑定FBO,在片段着色器中使用多个输出(每个渲染目标一个),并且不计算任何照明。您可以在OpenGL网站或通过快速谷歌搜索了解更多关于FBO和MRT的信息。然后,要照亮场景,您需要在着色器中读取该数据,并使用它来计算照明,就像正常情况下一样。最简单的方法(但不是最好的方法)是渲染场景的全屏四边形和采样颜色、法线和位置纹理

2) 几何体缓冲区是在场景中进行照明和其他着色所需的所有数据。它是在几何体过程中创建的(只有在几何体需要渲染时),通常是一组纹理。渲染几何体时,每个纹理都用作渲染目标(请参见上文关于FBO和MRT的内容)。通常,一个纹理用于颜色,一个用于法线,一个用于三维位置。如有必要,它还可以包含更多数据(如照明参数)。这将为您提供在照明过程中照亮每个像素所需的所有数据

伪代码可能如下所示:

   for all geometry {
      render to FBO
   }
   for all lights {
      read FBO and do lighting
   }
   //... here you can read the FBO and use it for anything!

添加更具体的内容,以便开始。您需要具有多个附件的FBO以及着色器写入多个FBO附件的方式。谷歌
glDrawBuffers
。FBO附件还需要是纹理,以便将信息传递到着色器。FBO附件的大小应与要渲染到的屏幕的大小相同。有很多方法可以做到这一点。这里有一个例子

您需要两个FBO

几何缓冲区

1. Diffuse (GL_RGBA)
2. Normal Buffer (GL_RGB16F)
3. Position Buffer (GL_RGB32F)
4. Depth Buffer
注意3)是一个巨大的浪费,因为我们可以使用深度缓冲和投影来重建位置。这个便宜多了。至少从位置缓冲区开始是一个好的开始。一次解决一个问题

2)普通缓冲区也可以进一步压缩

光积累缓冲器

1. Light Buffer (GL_RGBA)
2. Depth Buffer
此FBO中的深度缓冲区附件应与几何体缓冲区中的附件相同。在本例中,我们可能不使用此深度缓冲区信息,但您迟早会需要它。它将始终包含第一阶段的深度信息

我们如何呈现这些内容?

我们首先使用非常简单的着色器渲染场景。其目的主要是填充几何缓冲区。我们只是用一个非常简单的着色器填充几何缓冲区来绘制所有的几何体。为了简单起见,我使用了120个着色器,并且没有纹理贴图(尽管添加纹理贴图非常简单)

顶点着色器:

#version 120

varying vec3 normal;
varying vec4 position;

void main( void )
{
    normal = normalize(gl_NormalMatrix * gl_Normal);
    position = gl_ModelViewMatrix * gl_Vertex;
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
片段着色器:

#version 120

uniform vec4 objectColor; // Color of the object you are drawing
varying vec3 normal;
varying vec4 position;

void main( void )
{
    // Use glDrawBuffers to configure multiple render targets
    gl_FragData[0] = objectColor; // Diffuse
    gl_FragData[1] = vec4(normalize(normals.xyz), 0.0); // normals
    gl_FragData[2] = vec4(position.xyz, 0.0); // Position
}
例如,我们现在已经绘制了20个对象,以输出具有不同颜色的几何缓冲区。如果我们看一下漫反射缓冲区,它是一个非常单调的图像,颜色很简单(或者没有照明的纹理很简单),但是我们仍然可以看到每个片段的视图位置、法线和深度。这将是下一阶段进行照明时的重要信息

光积累

现在我们切换到我们的光积累缓冲区,是时候做一些光魔法了。对于每个单独的灯光w
uniform sampler2D diffuseBuffer;
uniform sampler2D positionBuffer;
uniform sampler2D normalBuffer;

uniform float lightRadius; // Radius of our point light
uniform vec3 lightPos; // Position of our point light
uniform vec4 lightColor; // Color of our light
uniform vec2 screensize; // screen resolution

void main()
{
    // VU for the current fragment
    vec2 uv = vec2(gl_FragCoord.x / screensize.x, gl_FragCoord.y / screensize.y);
    // Read data from our gbuffer (sent in as textures)
    vec4 diffuse_g = texture2D(diffuseBuffer, uv);
    vec4 position_g = texture2D(positionBuffer, uv);
    vec4 gnormal_g = texture2D(normalBuffer, uv);

    // Distance from the light center and the current pixel
    float distance = length(lightPos - position_g.xyz);

    // If the fragment is NOT affecter by the light we discard it!
    // PS : Don't kill me for using discard. This is for simplicity.
    if(distance > lightRadius) discard;

    // Calculate the intensity value this light will affect the fragment (Standard light stuff!)
    ... Use lightPos and position_g to calculate the light normal ..
    ... Do standard dot product of light normal and normal_g ...
    ... Just standard light stuff ...

    // Super simple attenuation placeholder
    float attenuation = 1.0 - (distance / lightRadius);

    gl_FragColor = diffuse_g * lightColor * attenuation * <multiplier from light calculation>;
}