延迟着色究竟如何在LWJGL中起作用?

bit*_*AKE 4 java opengl glsl deferred-rendering

我想用GLSL,Java和openGl开始一个延迟着色项目

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

我见过一些代码片段,那里没有多个渲染循环.

2.什么是几何缓冲区,它有什么作用?它是否类似于场景数据的存储,我可以绘制到纹理而不再渲染?

Gri*_*mmy 13

要添加更具体的内容,您可能会开始使用.您需要具有多个附件的FBO以及着色器可以写入多个FBO附件的方法.谷歌glDrawBuffers.您的FBO附件也需要是纹理,因此可以将信息传递给着色器.FBO附件的大小应与要渲染的屏幕大小相同.有很多方法可以解决这个问题.这是一个例子.

你需要两个FBO

几何缓冲区

1. Diffuse (GL_RGBA)
2. Normal Buffer (GL_RGB16F)
3. Position Buffer (GL_RGB32F)
4. Depth Buffer
Run Code Online (Sandbox Code Playgroud)

请注意3)是一个巨大的浪费,因为我们可以使用深度缓冲区和投影来重建位置.这便宜很多.开始使用位置缓冲区至少是一个好的开始.一次攻击一个问题.

2)普通缓冲区也可以进行更多压缩.

光累积缓冲液

1. Light Buffer (GL_RGBA)
2. Depth Buffer
Run Code Online (Sandbox Code Playgroud)

此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;
}
Run Code Online (Sandbox Code Playgroud)

片段着色器:

#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
}
Run Code Online (Sandbox Code Playgroud)

我们现在已经例如将20个对象绘制成具有不同颜色的几何缓冲区.如果我们看一下漫反射缓冲区,它是一个非常暗淡的图像,带有纯色(或没有光照的普通纹理),但我们仍然有每个单个片段的视图位置,正常和深度.这对于进行照明时的下一阶段是有价值的信息.

光累积

现在我们切换到我们的光累积缓冲区,现在是时候做一些光魔法了.对于每个单独的光,我们将绘制到我们的光累积缓冲区,并启用了添加混合.只要你覆盖受光影响的所有碎片,你如何做到这一点并不重要.你最初可以通过绘制一个全屏四边形来做到这一点,但这是非常昂贵的.我们只会覆盖点光源,但这足以涵盖简单的照明原理(简单的点光源非常简单).一种简单的方法是在由光半径缩放的光位置处绘制立方体或低多边形球(光量).这使得渲染大量的小灯更有效..但现在不要担心性能.Fullscreen quad会很好地完成这个技巧.

现在,简单的原则是:

  • 每个片段都有一个存储的x,y,z位置,我们只需使用纹理获取
  • 我们传递光的位置
  • 我们传入光的半径
  • 我们可以通过测量距位置缓冲器中的值与光位置的距离来知道片段是否受光影响
  • 从那以后它是非常标准的光计算

片段着色器:(此着色器适用于任何事物.轻量级,全屏四边形......无论如何)#version 120

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>;
}
Run Code Online (Sandbox Code Playgroud)

我们为每一盏灯重复这个.灯光的渲染顺序无关紧要,因为添加混合的结果总是相同的.通过仅累积光强度,您也可以更简单地做到这一点.理论上,您应该已经在光累积缓冲区中获得了最终的点亮结果,但您可能希望进行其他调整.

结合

您可能想要调整一些事情.周围?色彩校正?多雾路段?其他后期处理的东西.您可以将光累积缓冲区和漫反射缓冲区组合在一起进行一些调整.我们已经在光照阶段就已经做到了,但是如果你只保存光强度,那么你必须在diffuse * light这里做一个简单的组合.

通常只是一个全屏四边形,可以将最终结果呈现给屏幕.

更多东西

  • 如前所述,我们希望摆脱位置缓冲区.在投影中使用深度缓冲区来重建位置.
  • 不需要使用轻量级.有些人更喜欢简单地渲染一个足够大的四边形来覆盖屏幕上的区域.
  • 上面的示例不包括如何为每个对象定义唯一材料的问题.gbuffer格式有很多资源和变种.有些人喜欢在alpha通道中保存材质索引(在漫反射缓冲区中),然后在纹理中查找一行以获取材质属性.
  • 通过将全屏四边形渲染到光累积缓冲区中,可以轻松地处理影响整个场景的方向光和其他光类型
  • 聚光灯也很好,也很容易实现
  • 我们可能想要更多的光属性
  • 我们可能想要一些方法来衡量漫反射和光缓冲结合如何组合以支持环境和发射
  • 有许多方法可以更紧凑的方式存储法线.例如,您可以使用球面坐标删除一个值.有很多关于延迟照明和gbuffer格式的文章.查看人们使用的格式可以为您提供一些想法.只要确保你的gbuffer不要太胖.
  • 使用线性化深度值和您的投影重建视图位置并不难.您需要使用投影常量构造矢量.将其与深度值(0到1之间)相乘以获得视图位置.那里有几篇文章.这只是两行代码.

在这篇文章中可能有很多内容可以选择,但希望它能说明一般原则.没有人编写着色器.这只是通过内存从3.3转换为1.2.

有几种光积累的方法.您可能希望减少绘制调用的次数,使VBO具有1000个立方体和锥体来批量绘制所有内容.使用更现代的GL版本,您还可以使用几何着色器计算四边形,以覆盖每个灯光的亮区.可能最好的方法是使用计算着色器,但这需要GL 4.3.这里的优点是您可以迭代所有光信息并进行一次写入.还有伪计算方法是将屏幕划分为粗略网格并为每个单元格指定灯光列表.这只能通过片段着色器完成,但需要您在CPU上构建灯光列表,并通过UBO将数据发送到着色器.

计算着色器方法是迄今为止最简单的方法.它消除了旧方法中的许多复杂性,以便跟踪和组织所有内容.只需迭代灯光,然后对帧缓冲区进行一次写入.